Yaratımsal Kalıplar — Constructor Ne Zaman Yetmez
Önce şunu oku: Value Object ve Record — Anlamı Tipe Taşımak
30 saniyede özet
Bir nesneyi kurmanın birkaç yolu var. İki üç alan için constructor ya da record yeter; nesneye bir ad gerekiyorsa static factory, çok sayıda isteğe bağlı ayar varsa builder. Hangisi olursa olsun, hatalı nesne hiç doğmamalı.
Ödeme servisine bağlanmak 30 saniye sürüyor, cevap beklemek ise 2 saniye. Niyet tam tersiydi. Kodda hata yok, derleyici de bir şey demedi — iki int sadece yer değiştirmişti.
-
Bayt: Ödeme servisine bağlanmak otuz saniye, cevap beklemek iki saniye sürüyor. Tam tersini istemiştim!
-
Sen: Kodda hata var mı?
-
Bayt: Yok. İki sayı sadece yer değiştirmiş, derleyici de fark etmemiş.
-
Bayt: Kahveciye sayı değil isim söyle: orta, yulaf, iki şot. Nesneler de öyle.
Constructor ne zaman yetmez?
new HttpConfig("https://pay", 30_000, 2_000, 3) — hangi sayının bağlantı süresi olduğunu kodu açmadan söyleyebilir misin? Cevabı göster
Söyleyemezsin. Anlam argümanların sırasına gömülü. Yerlerini değiştirmek de derlenir; hata ancak üretimde görünür.
Adım adım oku
- Constructor dört değer alıyor ve hangisinin ne olduğu yalnızca sıralarından anlaşılıyor.
- İki zaman aşımı yer değiştirirse kod yine derlenir; bağlantı otuz, okuma iki saniye olur.
- Builder'da her ayar adıyla verilir: connectTimeout, readTimeout, retries.
- build() çağrıldığında nesne bir kez kontrol edilir; hatalı bir nesne hiç doğmaz.
Her isteğe bağlı ayar için bir constructor daha eklemeye telescoping constructorHer isteğe bağlı parametre için bir öncekini çağıran yeni bir constructor eklemek. Çağrı noktası uzun, konumsal ve aynı tipteki argümanlar kolayca yer değiştirir.Sözlükte gör → denir. Az sayıda ve farklı tipte alan için constructor iyidir; çok sayıda isteğe bağlı ayarda değil.
Kafam karıştı, daha basit anlat
new Card(5000, 1000, true, false) yazınca hangi sayının ne olduğu kaybolur. Yerlerini karıştırırsan derleyici de fark etmez.
`new HttpConfig("https://pay", 2000, 30000, 3, true, false)` gibi bir çağrının asıl sorunu ne?
Ödeme servisine yapılan çağrılar 30 saniye boyunca bağlanmaya çalışıp sonra 2 saniyede okuma timeout'u alıyor. Niyet tam tersiydi. Hangi satırlar sorunun kaynağı?
Static factory: adı olan constructor
Bir static factory methodnew yerine nesneyi döndüren adı olan bir static metot: List.of, Integer.valueOf, Money.of. Hazır bir örnek ya da bir alt tip dönebilir.Sözlükte gör →, new yerine nesneyi döndüren adı olan bir metottur: Duration.ofSeconds(30), List.of(a, b), Money.of(10, TRY).
- Bir adı var:
ofSecondsileofMinutesaynı tipte, farklı niyetler. - Her seferinde yeni nesne üretmek zorunda değil:
Integer.valueOf(127)önbellekten döner. - Alt tip dönebilir:
List.ofuygun bir iç sınıfı seçer.
Kafam karıştı, daha basit anlat
Static factory, adı olan bir kapıdır: Duration.ofSeconds(30) ne yaptığını adıyla söyler. new ise her zaman aynı adı taşır.
`Money.of(amount, currency)` gibi bir static factory metodunun constructor'a göre üstünlükleri hangisinde doğru sıralanmış?
Program ne yazdırır?
Tek örnek: Singleton
Bazen bir şeyden yalnızca bir tane olmalı: tek bir ücret tablosu, tek bir saat. getInstance() da bir static factory’dir; her çağrıda aynı nesneyi verir.
if (instance == null) instance = new RateTable(); — iki thread bu satıra aynı anda gelirse kaç RateTable oluşur? Cevabı göster
İki tane olabilir. İkisi de null görür, ikisi de new der. Kilitsiz tembel kurulum bir yarıştır.
İlk akla gelen çare getInstance()’ı synchronized yapmak. Doğru çalışır, ama nesne kurulduktan sonra bile her çağrı kilitten geçer.
Daha sade iki yol var. Holder idiom işi sınıf yüklemeye bırakır: JVM bir sınıfı tek sefer ve güvenle başlatır. Tek elemanlı enum ise serileştirmede ve reflection’da bile ikinci bir örneğe izin vermez.
public final class RateTable { private RateTable() { /* load rates once */ }
// Holder idiom: Holder is initialised on first access, once, by the JVM. private static final class Holder { static final RateTable INSTANCE = new RateTable(); }
public static RateTable getInstance() { return Holder.INSTANCE; }}
// Enum singleton: safe against serialization and reflection too.enum SystemClock { INSTANCE; }Double-checked locking ve volatile· istersen atla
Double-checked locking önce kilitsiz bakar; null ise kilidi alıp bir kez daha bakar. Alan mutlaka volatile olmalı, yoksa başka bir thread yarım kurulmuş bir nesnenin referansını görebilir. Nedeni yeniden sıralamadır; JMM dersinde adım adım var.
Spring kullanıyorsan çoğu zaman hiçbirine gerek yok. Bean’ler varsayılan olarak singleton scopeVarsayılan bean kapsamı: konteyner başına tek örnek, tüm enjeksiyon noktaları aynı nesneyi paylaşır.Uygulama başına değil, konteyner başına tektir. Paylaşıldığı için singleton bir bean'e istek verisi yazmak, o veriyi tüm kullanıcılara dağıtmak demektir.Sözlükte gör →’tadır: tek örneği konteyner tutar ve sana enjekte eder.
Yoğun bir sabah loglarda iki kez "ücret tablosu yüklendi" görüyorsun, oysa RateTable tek olmalıydı. Hangi satırlar?
Double-checked locking ile yazılmış bir singleton'da `instance` alanı neden `volatile` olmalı?
Kendin gör
Nesne oluşturma — constructor ne zaman yetmez?
Tohum 280788var config = new HttpConfig("https://pay", 2_000, 30_000, 3, true, true);- Çağrı okunur mubekliyor
- Yer değiştiren argüman derlenir mibekliyor
- Nesne değiştirilemez mibekliyor
- Alanlar arası kural tek yerde denetlenir mibekliyor
- Önbellekten ya da alt tip dönebilir mibekliyor
- Yazılacak ek kodbekliyor
Şu an ne oldu?
Teleskop constructor, 5 isteğe bağlı ayar
Aynı HttpConfig nesnesi altı ölçüte göre değerlendirilecek.
Görevler0/4
İki argümanın yer değiştirmesinin derlendiği bir çağrı üretaçık
İpucu
Konumsal argümanlar ve aynı tipte yan yana değerler.
Nesnenin yarım hâlde görülebildiği bir kurulum seçaçık
İpucu
Önce boş nesne, sonra alanlar tek tek.
Builder'ın gereksiz olduğu bir durum bulaçık
İpucu
Builder her zaman güvenlidir — ama her zaman gerekli değildir.
6+ isteğe bağlı ayar için bütün ölçütleri yeşile çeviraçık
İpucu
Adı olan argümanlar, tek bir doğrulama anı ve değiştirilemez bir sonuç.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. Teleskop constructor, beş ayar: okunmuyor ve argümanlar yer değiştirebiliyor.
- Setter’ları seç. Adlar var, ama nesne yarım hâlde dolaşıyor.
- Builder’a geç. Bütün ölçütler yeşil.
- Ayar sayısını 2 yap. Builder hâlâ doğru, ama kazancından pahalı.
Her durum için en uygun oluşturma biçimini seç.
Satır satır: doğrulayan bir builder
Geçersiz nesne hiç oluşmaz
public record HttpConfig(String baseUrl, Duration connect, Duration read, int retries) { public HttpConfig { if (connect.compareTo(read) > 0) throw new IllegalArgumentException("connect > read"); } public static Builder builder(String baseUrl) { return new Builder(baseUrl); } public static final class Builder { private final String baseUrl; private Duration connect = Duration.ofSeconds(2), read = Duration.ofSeconds(30); private int retries = 0; private Builder(String baseUrl) { this.baseUrl = baseUrl; } public Builder read(Duration value) { this.read = value; return this; } public HttpConfig build() { return new HttpConfig(baseUrl, connect, read, retries); } }}Debug
çağıran Zorunlu değer builder'ın başlangıcında isteniyor: baseUrl'süz bir kurulum başlayamaz.
Sol/sağ ok tuşlarıyla da gezebilirsin.
builderÇok sayıda isteğe bağlı ayarı adıyla toplayıp nesneyi tek seferde, doğrulayarak kuran yardımcı. Geçersiz ya da yarım nesne hiç oluşmaz.Sözlükte gör →’ın asıl değeri adlar ve tek doğrulama anıdır. Kuralı record’un compact constructor’ına koy: builder da, doğrudan new de aynı kapıdan geçer.
Banka kartındaki ayarlar da böyle bir kahve siparişine benzer: günlük harcama limiti, ATM limiti, temassız limit, internet ve yurt dışı izni. Gel, bunları adıyla kuran küçük bir örneğe bakalım.
Derinleş · Kart ayarları: static factory ve builder 4 dosya · ~87 satır · ilk okumada atlayabilirsin
Kafam karıştı, daha basit anlat
Builder bir kahve siparişi gibidir: “şeker yok, süt var” diye adıyla söylersin. Sipariş en sonda tek bir yerde kontrol edilir.
Bir builder'da "connectTimeout, readTimeout'tan büyük olamaz" kuralı nerede denetlenmeli?
Tuzaklar
İki alan için builder. Record ya da constructor yeterken builder yalnızca kod ekler.
Boş constructor + setter. Nesne setter’lar arasında geçersiz ve her zaman değiştirilebilir.
Doğrulamayı atlayan üretilmiş builder. Lombok @Builder kuralları kendiliğinden bilmez; kuralı constructor’a koy.
Static factory ile Factory Method’u karıştırmak. GoF kalıbında hangi nesnenin oluşacağına alt sınıf karar verir; Spring’de bu işi çoğu zaman konteyner yapar.
GoF'un Factory Method kalıbı ile bir static factory metodu arasındaki fark ne?
Kendini sına
Aynı tipte iki constructor parametresinin yeri değişirse derleyici uyarır.
Boş constructor + setter'larla kurulan bir yapılandırma nesnesi, uygulama açılırken bir thread'de dolduruluyor ve başka thread'lerle paylaşılıyor. Builder + final alanlara geçmenin en önemli kazancı ne?
Aklında kalacak üç şey
- 1 Sırayla verilen argümanlar anlamı sıraya gömer. Aynı tipteki iki argüman yer değiştirse de kod derlenir.
- 2 Static factory'nin bir adı vardır, hazırdakini geri verebilir ve bir alt tip döndürebilir. Integer.valueOf ve List.of bunun örnekleri.
- 3 Builder çok sayıda isteğe bağlı ayarı adıyla toplar ve nesneyi build() anında bir kez kontrol eder. İki üç alan için gereksiz yüktür.
5 kart sonraki derste seni bekliyor