İçeriğe geç

DRY, KISS, YAGNI ve Demeter — Değişimi Ucuz Tutmak

Orta 10 dk Çok sık karşılaşılır

Önce şunu oku: Temiz Kod — İsimler, Fonksiyonlar, Yorumlar

30 saniyede özet

DRY, bir bilginin tek sahibi olsun der; tesadüfen aynı görünen kodu birleştirmek ise yanlış soyutlamadır. KISS ve YAGNI bugün gerekmeyeni yazma der. Demeter, uzaktaki nesnelerin içini ezberleme der.

Evdeki Wi-Fi şifresini değiştirdin. Buzdolabındaki not, misafir odasındaki kart ve annenin telefonu hâlâ eski şifreyi söylüyor. Kodda da aynısı olur: bir bilgi kaç yerde yazılıysa, değiştiğinde o kadar yeri bulman gerekir.

Aynı bilgi dört kopya mı, tek sahip mi? Fark, bilgi değiştiği gün çıkar.
Adım adım oku
  1. KDV oranı dört ayrı dosyada elle yazılmış.
  2. Oran değişiyor. Dört kopyanın dördünü de bulman gerekiyor.
  3. Biri 18 / 100 diye farklı yazılmış: aramada çıkmıyor ve eski oranla hesaplamaya devam ediyor.
  4. Oranın tek bir sahibi olsaydı: tek satır değişir, dört dosya da yeni oranı görür.
  1. Bayt: KDV oranı değişti. Dört dosyada güncelledim, iadeler hâlâ eski oranla hesaplanıyor!

  2. Sen: Dördünü de bulmadın mı?

  3. Bayt: Üçünü buldum. Biri 18 / 100 diye yazılmıştı, aramada çıkmadı.

  4. Bayt: Değişimi ucuz tutan dört küçük kural var. Tek tek gideceğiz.

DRY: bir bilgi, bir sahip

DRY"Don't repeat yourself": aynı sebeple değişen her bilginin sistemde tek bir sahibi olmalı. Kod satırlarıyla değil, bilgiyle ilgilidir.Sözlükte gör →, aynı satırı iki kez yazmamak değil, aynı bilginin tek bir sahibi olması demektir. KDV oranı bir bilgidir; dört dosyada yazılıysa dört sahibi vardır.

Önce: dört kopya, biri farklı yazılmış
// OrderService, InvoicePrinter, CartSummary
BigDecimal vat = amount.multiply(new BigDecimal("0.18"));
// RefundService: the same fact, spelled differently
BigDecimal vat = amount.multiply(BigDecimal.valueOf(18)).divide(BigDecimal.valueOf(100));
Sonra: tek sahip
public final class Vat {
public static final BigDecimal RATE = new BigDecimal("0.20");
private Vat() {}
}

Peki aynı görünen her şey aynı bilgi midir?

Ücretsiz kargo sınırı 500 TL. Şüpheli siparişlerin elle incelenme sınırı da 500 TL. İkisini tek bir sabitte birleştirmek iyi bir DRY mı? Cevabı göster

Hayır. İki sayı tesadüfen aynı; farklı ekipler, farklı sebeplerle değiştirir. Birleştirirsen kargo sınırını 750 yaptığın gün inceleme sınırı da sessizce 750 olur.

Buna yanlış soyutlama denir: iki bağımsız kuralı tek bir düğmeye bağlamak. Tekrar silinmedi, iki kural birbirine kilitlendi.

Pratik bir ölçü üç kuralı: iki benzer kodu hemen birleştirme. Üçüncüsü geldiğinde neyin gerçekten ortak olduğu görünür.

Kafam karıştı, daha basit anlat

Bir bilgi tek bir yerde yazılsın. Ama iki şey yalnızca aynı görünüyorsa, onları birleştirmek tekrar silmek değil, onları birbirine bağlamaktır.

Hızlı kontrolBaşlangıç

DRY (Don't Repeat Yourself) ilkesi asıl olarak neyin tekrarlanmamasını ister?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Ücretsiz kargo sınırı ve şüpheli sipariş inceleme sınırı ikisi de 500 TL. Biri ikisini `Limits.THRESHOLD` adlı tek bir sabite birleştirdi. Kargo sınırı 750 TL'ye çıkınca ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Demeter: yakınındakiyle konuş

Gazeteci çocuk parasını almak için cüzdanını cebinden çıkarıp içini karıştırmaz. Sen ödersin; cüzdanın deri mi, kartlı mı, onu ilgilendirmez.

Law of DemeterBir metot yalnızca kendisiyle, alanlarıyla ve parametreleriyle konuşsun; onların döndürdüğü nesnelerin içine uzanmasın. a.getB().getC() zincirleri bunu çiğner.Sözlükte gör → bunu koda taşır: bir metot kendi alanlarıyla ve parametreleriyle konuşsun, onların döndürdüğü nesnelerin içine uzanmasın.

Tren kazası
String city = order.getCustomer().getAddress().getCity();

Bu tek satır üç sınıfın iç yapısını ezberliyor. Müşterinin adresi ikiye ayrıldığı gün, bu satırı yazan her dosya kırılır.

Yakın kuzeni tell, don't askBir nesneden veriyi isteyip kararı dışarıda vermek yerine, nesneye ne istediğini söylemek. Kural, koruduğu verinin yanında kalır.Sözlükte gör →: nesneden veriyi isteyip kararı dışarıda verme. Ne istediğini söyle, kararı nesne versin.

Sormak yerine söylemek

TransferService.java
1void pay(Account account, BigDecimal amount) {
şu an çalışan satır if (account.getBalance().compareTo(amount) >= 0) {
3 account.setBalance(account.getBalance().subtract(amount));
4 } else {
5 throw new InsufficientFundsException();
6 }
7}

Debug

Adım 1/4

servis Servis bakiyeyi soruyor ve kararı kendisi veriyor. Kural Account'ın değil, servisin içinde.

kural nerede
= TransferService
Java 21UTF-8LF2:1

Sol/sağ ok tuşlarıyla da gezebilirsin.

Derinleş · Hesaptan çekim: sormadan söylemek 3 dosya · ~44 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/java/com/bank/account/ Account.java Kural verinin yanında: hesap kendi bakiyesini kendisi korur.

src/main/java/com/bank/account/Account.java
public class Account {
private final Iban iban;
private Money balance;
public Account(Iban iban, Money openingBalance) {
this.iban = iban;
this.balance = openingBalance;
}
// Tell, don't ask: the rule lives next to the data it protects.
public void withdraw(Money amount) {
if (amount.isGreaterThan(balance)) {
throw new InsufficientFundsException(iban, amount);
}
balance = balance.minus(amount);
}
}

src/main/java/com/bank/transfer/ TransferService.java Servis yalnızca ne istediğini söyler; hesabın içini bilmez.

src/main/java/com/bank/transfer/TransferService.java
@Service
public class TransferService {
private final AccountRepository accounts;
TransferService(AccountRepository accounts) {
this.accounts = accounts;
}
@Transactional
public void transfer(Iban from, Iban to, Money amount) {
accounts.get(from).withdraw(amount); // one friend, one message
accounts.get(to).deposit(amount);
}
}

src/main/java/com/bank/transfer/legacy/ TransferService.java Şöyle de yazılabilirdi: servis bakiyeyi sorar, kararı kendisi verir, sonucu geri yazar. Çalışır, ama her çağıran kuralı yeniden yazar.

src/main/java/com/bank/transfer/legacy/TransferService.java
// Another way to write it: ask for the data, decide outside, write it back.
public void transfer(Iban from, Iban to, Money amount) {
Account source = accounts.get(from);
if (source.getBalance().isGreaterThan(amount) || source.getBalance().equals(amount)) {
source.setBalance(source.getBalance().minus(amount));
} else {
throw new InsufficientFundsException(from, amount);
}
Account target = accounts.get(to);
target.setBalance(target.getBalance().plus(amount));
}
Kafam karıştı, daha basit anlat

Uzaktaki nesnelerin içini karıştırma. Yakınındakine ne istediğini söyle; nasıl yapılacağını o bilir.

Hızlı kontrolOrta

Aşağıdakilerden hangisi Law of Demeter'i çiğner?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Servis şunu yapıyor: `if (account.getBalance() >= amount) account.setBalance(account.getBalance() - amount);`. Tell, don't ask'e göre daha iyi hâli hangisi?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

KISS ve YAGNI: bugün gerekeni yaz

KISS (keep it simple) işi gören en sade çözümü seçmektir. YAGNI"You aren't gonna need it" — gelmeyen değişime bugünden hazırlanma. SOLID ile gerçek bir gerilimi vardır ve ikisi de haklıdır.Sözlükte gör → ise onun zaman boyutu: gelmemiş bir istek için bugünden kod yazma.

Bir ekip “ileride başka alanlar da gelir” diye siparişe genel bir özel alan çerçevesi kurdu: eklenti arayüzü, kayıt defteri, ayar dosyası. Yıllarca tek bir alan eklenmedi.

İlk gerçek istek bir hediye notuydu. Düz bir alanla üç dosya sürecekti; çerçeve yüzünden yedi dosyaya dokunuldu.

Kafam karıştı, daha basit anlat

Bugün gerekmeyen şeyi yazma. Gerçekten gerektiğinde eklemek, şimdiden tahmin edip yanlış kurmaktan ucuzdur.

Hızlı kontrolBaşlangıç

Bir ekip, hiçbir gereksinim yokken 'ileride başka alanlar da gelir' diye siparişe genel bir eklenti çerçevesi kuruyor. Bu hangi ilkeyle çelişir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Simülatör aynı küçük sipariş kodunda bir istek değiştiriyor ve her dosyaya ne olduğunu gösteriyor.

Bir gereksinim değişince kaç dosyaya dokunuyorsun?

Tohum 79909

Yeni istek: KDV oranı %18 yerine %20 olacak.

RefundService.java
1// four files, four copies of the same fact
2BigDecimal vat = amount.multiply(new BigDecimal("0.18"));
3// ...and one written differently:
4BigDecimal refundVat = amount.multiply(BigDecimal.valueOf(18)).divide(BigDecimal.valueOf(100));
Java 21UTF-8LF
  • OrderService.javabekliyor
  • InvoicePrinter.javabekliyor
  • CartSummary.javabekliyor
  • RefundService.javabekliyor
Hız
Adım 0

Şu an ne oldu?

KDV oranı %18 yerine %20 olacak.

Adımla: her dosyada bu değişikliğin ne yaptığını gör. Say: kaç dosya açıldı, kaçı sessizce etkilendi?

Görevler0/3

  • Bir gereksinimi tek dosyaya dokunarak ve sürprizsiz karşılaaçık

    İpucu

    Bilginin tek bir sahibi olduğu bir tasarım seç.

  • Tek bir iç değişiklikle dört çağrı yerini birden kıraçık

    İpucu

    order.getCustomer().getAddress()... zinciri kaç yerde yazılı?

  • Tekrarı birleştir ve alakasız bir kuralı sessizce değiştiraçık

    İpucu

    İki kural aynı sayıyı tesadüfen paylaşıyorsa ve onları tek sabite bağlarsan?

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. KDV dört kopya: üçü düzenleniyor, RefundService gözden kaçıyor.
  2. Kodu “Sonra” yap. Tek dosya, sürpriz yok.
  3. “DRY tuzağı”nı seç ve iki kodu karşılaştır. Birleştirilmiş sabit, inceleme kuralını sessizce değiştiriyor.
  4. “Demeter”i seç. Önce dört dosya derlenmiyor; sonra değişiklik Order’da bitiyor.
  5. “YAGNI”yi seç. Çerçeveli sürüm yedi, düz sürüm üç dosya açıyor.
Hızlı kontrolOrta

Simülatörde KDV oranı değişince `RefundService` neden gözden kaçıyor?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Her tekrarı silmek. Testlerde biraz tekrar çoğu zaman iyidir: her test tek başına okunur.

Nokta saymak. Stream ve builder zincirleri hep aynı nesneyle konuşur; ezberlenen bir iç yapı yok, Demeter çiğnenmiyor.

Aracıyı şişirmek. Her zincir için Order’a bir iletici metot eklemek onu başkalarının işini taşıyan bir aracıya çevirir. İleticiyi değil, davranışın kendisini taşı.

YAGNI’yi bahane etmek. Yazılı, bilinen bir gereksinimi “belki gelmez” diye ertelemek YAGNI değildir. Ayrıntılı temizlik hamleleri için Code Smell ve Refactoring dersine bakabilirsin.

Kendini sına

Şimşek turu1/5

DRY, aynı kod satırının iki kez yazılmamasıdır.

Soru 1/4İleri

İki yıl önce üç benzer metot tek bir `calculateTotal(order, isRefund, isExport, skipTax)` metoduna birleştirilmiş. Her çağıran farklı bir bayrak karışımı geçiyor ve her yeni istek bir bayrak daha ekliyor. En sağlıklı yol hangisi?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 DRY kod satırlarıyla değil bilgiyle ilgilidir: aynı sebeple değişen bir bilginin tek bir sahibi olmalıdır.
  2. 2 Aynı görünen ama farklı sebeplerle değişen iki kodu birleştirmek yanlış bir soyutlamadır. Üçüncü tekrarı beklemek çoğu zaman daha ucuzdur.
  3. 3 Uzun bir getter zinciri, çağıranın başka nesnelerin iç yapısını ezberlediğini gösterir. Nesneye ne istediğini söylemek, kırılan yeri tek sınıfa indirir.
Sonraki kapı Müşteri hesap açamadı; ekranda hata gördü. Beş saniye sonra telefonuna "Bankamıza hoş geldiniz" e-postası geldi. Nasıl? Domain Olayları — Hesap Açılmadı, Hoş Geldin E-postası Neden Gitti? · 8 dk

4 kart sonraki derste seni bekliyor

0/5 kart bu dersten toplandı