Yapısal Kalıplar — Sarmalayarak Genişletmek
Önce şunu oku: Tasarım Kalıpları — Ne Zaman Kazandırır
30 saniyede özet
Decorator bir nesneyi hediye paketi gibi sarar ve ona davranış ekler; Adapter yabancı bir arayüzü kendi dilimize çevirir; Facade karmaşık bir sisteme tek bir kapı açar. Sarmalarken sıra, kimin neyi göreceğini belirler.
İzleme ekranı fiyat servisinde %50 hata gösteriyor. Kullanıcılardan tek bir şikâyet yok. İkisi de doğru — metriği ölçen katman sadece yanlış yerde duruyor.
-
Bayt: Dashboard fiyat servisinde koca bir hata oranı gösteriyor!
-
Sen: Kullanıcılar da şikâyet ediyor mu?
-
Bayt: Tek bir şikâyet bile yok! Hangisi yalan söylüyor?
-
Bayt: İkisi de doğru. Hediye paketinde kartın nereye konduğuna bakalım.
Decorator: aynı arayüz, ek davranış
Bir decoratorAynı arayüzü uygulayıp asıl nesneyi saran ve çağrının önüne ya da arkasına davranış ekleyen nesne. Birden fazlası üst üste dizilebilir; sıra, her katmanın ne gördüğünü belirler.Sözlükte gör →, asıl nesneyle aynı arayüzü uygular ve onu sarar. Çağıran farkı görmez; davranış çağrının etrafına eklenir.
PriceClient client = new MetricsPriceClient( new CachingPriceClient( new RetryingPriceClient(realClient)));Kalıtımla aynı şeyi yapmak her kombinasyon için ayrı bir sınıf isterdi. Decorator’larla her davranış tek sorumlu küçük bir sınıftır.
Kafam karıştı, daha basit anlat
Decorator, bir hediyeyi paketlemek gibidir: içindeki hediye aynı kalır, ama üstüne kurdele eklenir. Çağıran yine aynı kutuyu açtığını sanır.
Decorator kalıbının temel fikri nedir?
Loglama, önbellek ve retry'ı PriceClient'a kalıtımla eklemeye çalışsaydın ne olurdu?
Çevirmen ve resepsiyon: Adapter ve Facade
| Kalıp | Arayüz | Ne için |
|---|---|---|
| Decorator | Aynı kalır | Davranış eklemek: retry, cache, metrik |
| adapterBir sınıfın arayüzünü, kodunun beklediği başka bir arayüze çeviren ara nesne. Üçüncü parti bir SDK'yı kendi port'una uydurmak tipik örnek.Sözlükte gör → | Değişir | Yabancı bir API’yi kendi port’una çevirmek |
| facadeKarmaşık bir alt sistemin önüne, en sık kullanılan işleri basit metotlarla sunan tek bir giriş noktası koymak. Alt sistemi gizlemez, kullanımını kolaylaştırır.Sözlükte gör → | Yeni ve basit | Karmaşık bir alt sistemin sık kullanılan akışına tek giriş |
Adapter bir çeviridir. Stripe’ın tipleri adapter’ın içinde kalmalı; port yalnızca PaymentResult gibi kendi tiplerimizle konuşur.
Kafam karıştı, daha basit anlat
Adapter bir çevirmendir: dışarıdan gelen tuhaf dili bizim dilimize çevirir. Facade bir resepsiyondur: arkadaki karmaşık birimleri tek bir masanın arkasına saklar.
Her durumu en uygun yapısal kalıba yerleştir.
Kendin gör
Metrik katmanı retry'ın içinde. Gerçek servisin ilk denemesi düştü, retry ikinci denemede kurtardı. Kullanıcı cevabını aldı. Dashboard bir hata gösterir mi? Cevabı göster
Gösterir. Metrik her denemeyi ayrı bir çağrı sayar; retry’ın kurtardığını bilmez. Kullanıcının görmediği bir hata alarma dönüşür.
Adım adım oku
- İki yığında da aynı üç katman var: retry, metrik ve asıl servis. Tek fark sıraları.
- İlk deneme düşer, retry isteği tekrar dener ve ikinci deneme başarılı olur.
- Metrik retry'ın içindeyse her denemeyi görür ve düşen ilk denemeyi hata olarak sayar.
- Metrik en dıştaysa yalnızca kullanıcının gördüğünü sayar: başarılı bir cevap, sıfır hata.
Decorator sırası — her katman yalnızca kendisine ulaşanı görür
Tohum 199363PriceClient client = new CachingPriceClient(new RetryingPriceClient(new MetricsPriceClient(realClient))); // dıştan içe: CachingPriceClient → RetryingPriceClient → MetricsPriceClient → gerçek- Kullanıcı çağrısı 1
- Kullanıcı çağrısı 2
- Kullanıcı çağrısı 3
Şu an ne oldu?
Aynı ürünün fiyatı üç kez isteniyor
Gerçek istemcinin ilk denemesi geçici bir hatayla düşüyor. Her katman aynı arayüzü uyguluyor; hangi sıra olursa olsun derlenir.
Görevler0/3
Kullanıcıların hiç görmediği bir hatayı dashboard'da gösteraçık
İpucu
Retry hatayı kurtarsın, ama metrik denemeleri tek tek saysın.
Önbellek isabetlerini dashboard'dan gizleaçık
İpucu
Önbellek metriğin dışındaysa, isabetler metriğe hiç ulaşmaz.
Cache ve retry açıkken dashboard kullanıcıyla birebir aynı şeyi görsünaçık
İpucu
Kullanıcının yaşadığını ölçmek için metrik kullanıcıya en yakın katman olmalı.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. Dashboard %50 hata ve 2 çağrı görüyor; kullanıcılar 3 çağrı ve sıfır hata.
- “Retry → Metrics → Cache” seç. Önbellek isabetleri görünüyor, ama hata hâlâ var.
- “Metrics → Cache → Retry” seç. Dashboard kullanıcıyla aynı şeyi görüyor.
- Retry’ı kapat. Bu kez hata gerçek: ilk kullanıcı onu yaşadı.
Metrik decorator'ı retry decorator'ının içine yerleştirildi. Gerçek servisin ilk denemesi geçici bir hatayla düştü, retry kurtardı. Dashboard ne gösterir?
Önbellek decorator'ı metrik decorator'ının dışında. Dashboard'daki ortalama süre neden kullanıcıların yaşadığından yüksek görünür?
Satır satır: bir decorator yazmak
Sar, çağır, gerekirse tekrar çağır
final class RetryingPriceClient implements PriceClient { private final PriceClient delegate; RetryingPriceClient(PriceClient delegate) { this.delegate = delegate; } @Override public Price priceOf(String sku) { try { return delegate.priceOf(sku); } catch (TransientException first) { return delegate.priceOf(sku); } }}Debug
tasarım Aynı arayüzü uyguluyor. PriceClient bekleyen her yere bu da verilebilir.
Sol/sağ ok tuşlarıyla da gezebilirsin.
Sıra bir tasarım kararıdır. Kullanıcıyı ölçen metrik en dışta, bağımlılığın sağlığını ölçen metrik retry’ın içinde durur — adları da buna göre olmalı.
Bir bankanın döviz kuru servisinde üç kalıp yan yana durur. Piyasa verisi sağlayıcısını bir adapter çevirir, retry ve metrik de onu hediye paketi gibi sarar.
Derinleş · Döviz kuru servisi: adapter ve decorator'lar 4 dosya · ~61 satır · ilk okumada atlayabilirsin
Kafam karıştı, daha basit anlat
Paketlerin sırası önemlidir. Ölçümü en dışa koyarsan kullanıcının yaşadığını, retry’ın içine koyarsan her denemeyi ayrı ayrı ölçersin.
Ödeme sağlayıcısı değiştirilmek istendiğinde domain kodunun yarısına dokunmak gerekiyor. Bu adapter'daki sorun hangi satırda?
Tuzaklar
Sızan adapter. Port’un imzasında SDK tipi varsa, o SDK bütün koda yayılmıştır.
Her şeyi bilen facade. Sık kullanılan akışa basit giriş olmalıyken 40 metotlu bir sınıfa dönüşür.
Derin zincirde hata ayıklamak. Beş katmanlı bir decorator zincirinde stack trace uzar; her katmanın adı ne yaptığını söylemeli.
Self-invocation. Spring’in proxy’leri de birer sarmalayıcıdır; nesnenin kendi içinden yaptığı çağrı sarmalayıcıyı atlar.
Bir OrderFacade zamanla 40 metotlu, her servisi bilen ve her şeyi yapan bir sınıfa dönüştü. Sorun ne?
Kendini sına
Decorator, sardığı nesneyle aynı arayüzü uygular.
Retry ve önbellek decorator'larının sırası neden önemli olabilir? Bir önbellek başarısızlıkları da (örneğin null fallback) saklıyorsa ne olur?
Aklında kalacak üç şey
- 1 Decorator aynı arayüzü uygulayıp asıl nesneyi sarar. Kalıtımla her karışım için ayrı sınıf gerekirdi; sarmalayarak davranışları çalışırken birleştirirsin.
- 2 Adapter bir çeviridir: yabancı tipler adapter'ın içinde kalır, geri kalan kod yalnızca kendi diliyle konuşur.
- 3 Bir decorator yalnızca kendisine ulaşanı görür. Hata sayacını tekrar denemenin içine koymak, düzelen hataları alarma çevirir.
5 kart sonraki derste seni bekliyor