İçeriğe geç

Yapısal Kalıplar — Sarmalayarak Genişletmek

Orta 8 dk Sık karşılaşılır

Ö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.

  1. Bayt: Dashboard fiyat servisinde koca bir hata oranı gösteriyor!

  2. Sen: Kullanıcılar da şikâyet ediyor mu?

  3. Bayt: Tek bir şikâyet bile yok! Hangisi yalan söylüyor?

  4. 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.

Aynı arayüz, üç katman
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.

Hızlı kontrolBaşlangıç

Decorator kalıbının temel fikri nedir?

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

Loglama, önbellek ve retry'ı PriceClient'a kalıtımla eklemeye çalışsaydın ne olurdu?

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

Çevirmen ve resepsiyon: Adapter ve Facade

KalıpArayüzNe için
DecoratorAynı kalırDavranış 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şirYabancı 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 basitKarmaşı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.

Hızlı kontrolOrta

Her durumu en uygun yapısal kalıba yerleştir.

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

Sınıflandırılmamış

Decorator

Aynı arayüz, ek davranış

    Adapter

    Arayüz çevirisi

      Facade

      Karmaşık alt sisteme basit giriş

        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.

        Aynı katmanlar, farklı sıra: kim neyi görüyor?
        Adım adım oku
        1. İki yığında da aynı üç katman var: retry, metrik ve asıl servis. Tek fark sıraları.
        2. İlk deneme düşer, retry isteği tekrar dener ve ikinci deneme başarılı olur.
        3. Metrik retry'ın içindeyse her denemeyi görür ve düşen ilk denemeyi hata olarak sayar.
        4. 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 199363
        PriceClientConfig.java
        1PriceClient client =
        2 new CachingPriceClient(new RetryingPriceClient(new MetricsPriceClient(realClient)));
        3
        4// dıştan içe: CachingPriceClient → RetryingPriceClient → MetricsPriceClient → gerçek
        Java 21UTF-8LF
        1. Kullanıcı çağrısı 1
        2. Kullanıcı çağrısı 2
        3. Kullanıcı çağrısı 3
        Hız
        Adım 0

        Ş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.

        1. Varsayılanla oynat. Dashboard %50 hata ve 2 çağrı görüyor; kullanıcılar 3 çağrı ve sıfır hata.
        2. “Retry → Metrics → Cache” seç. Önbellek isabetleri görünüyor, ama hata hâlâ var.
        3. “Metrics → Cache → Retry” seç. Dashboard kullanıcıyla aynı şeyi görüyor.
        4. Retry’ı kapat. Bu kez hata gerçek: ilk kullanıcı onu yaşadı.
        Hızlı kontrolOrta

        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?

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

        Ö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?

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

        Satır satır: bir decorator yazmak

        Sar, çağır, gerekirse tekrar çağır

        RetryingPriceClient.java
        şu an çalışan satırfinal class RetryingPriceClient implements PriceClient {
        2 private final PriceClient delegate;
        3
        4 RetryingPriceClient(PriceClient delegate) { this.delegate = delegate; }
        5
        6 @Override
        7 public Price priceOf(String sku) {
        8 try {
        9 return delegate.priceOf(sku);
        10 } catch (TransientException first) {
        11 return delegate.priceOf(sku);
        12 }
        13 }
        14}

        Debug

        Adım 1/4

        tasarım Aynı arayüzü uyguluyor. PriceClient bekleyen her yere bu da verilebilir.

        Java 21UTF-8LF1:1

        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
        Proje dosyaları

        src/main/java/com/bank/fx/ FxRates.java Bankanın kendi port'u: yalnızca kendi tiplerimizle konuşur.

        src/main/java/com/bank/fx/FxRates.java
        // Only the bank's own types in the signature: Currency in, FxQuote out.
        // FxQuote is a record of bid, ask and asOf; no vendor class leaks through here.
        public interface FxRates {
        FxQuote quote(Currency base, Currency quote);
        }

        src/main/java/com/bank/fx/ MarketDataFxRates.java Adapter, sağlayıcının SDK tiplerini içeride tutar ve kuru BigDecimal'e çevirir.

        src/main/java/com/bank/fx/MarketDataFxRates.java
        // Adapter: the vendor SDK types stop here.
        final class MarketDataFxRates implements FxRates {
        private final MarketDataSession session; // vendor SDK
        MarketDataFxRates(MarketDataSession session) {
        this.session = session;
        }
        @Override
        public FxQuote quote(Currency base, Currency quote) {
        VendorTick tick = session.lastTick(base.getCurrencyCode() + quote.getCurrencyCode());
        // The vendor sends rates as text; new BigDecimal(String) keeps every digit.
        return new FxQuote(base, quote,
        new BigDecimal(tick.getBid()),
        new BigDecimal(tick.getAsk()),
        Instant.ofEpochMilli(tick.getTimestampMillis()));
        }
        }

        src/main/java/com/bank/fx/ MeasuredFxRates.java Metrik decorator'ı: aynı arayüz, etrafına bir sayaç.

        src/main/java/com/bank/fx/MeasuredFxRates.java
        final class MeasuredFxRates implements FxRates {
        private final FxRates delegate;
        private final MeterRegistry meters;
        private final String name;
        MeasuredFxRates(FxRates delegate, MeterRegistry meters, String name) {
        this.delegate = delegate;
        this.meters = meters;
        this.name = name;
        }
        @Override
        public FxQuote quote(Currency base, Currency quote) {
        try {
        FxQuote result = delegate.quote(base, quote);
        meters.counter(name, "outcome", "success").increment();
        return result;
        } catch (RuntimeException ex) {
        meters.counter(name, "outcome", "failure").increment();
        throw ex;
        }
        }
        }

        src/main/java/com/bank/fx/ FxRatesConfig.java Sarma sırası burada okunur; metrik en dışta, müşterinin gördüğünü sayar.

        src/main/java/com/bank/fx/FxRatesConfig.java
        @Configuration
        class FxRatesConfig {
        // Read inside out: vendor, adapter, retry, then the customer-facing metric.
        // The metric's name says what it counts: calls as the customer saw them.
        @Bean
        FxRates fxRates(MarketDataSession session, MeterRegistry meters) {
        return new MeasuredFxRates(
        new RetryingFxRates(new MarketDataFxRates(session)),
        meters, "fx.quote.customer");
        }
        }
        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.

        Hızlı kontrolOrta

        Ödeme sağlayıcısı değiştirilmek istendiğinde domain kodunun yarısına dokunmak gerekiyor. Bu adapter'daki sorun hangi satırda?

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

        Hatalı satıra dokun, sonra kontrol et.

        StripePaymentAdapter.java
        Java 21UTF-8LF

        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.

        Hızlı kontrolOrta

        Bir OrderFacade zamanla 40 metotlu, her servisi bilen ve her şeyi yapan bir sınıfa dönüştü. Sorun ne?

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

        Kendini sına

        Şimşek turu1/5

        Decorator, sardığı nesneyle aynı arayüzü uygular.

        Soru 1/2İleri

        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?

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

        Aklında kalacak üç şey

        1. 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. 2 Adapter bir çeviridir: yabancı tipler adapter'ın içinde kalır, geri kalan kod yalnızca kendi diliyle konuşur.
        3. 3 Bir decorator yalnızca kendisine ulaşanı görür. Hata sayacını tekrar denemenin içine koymak, düzelen hataları alarma çevirir.
        Sonraki kapı Üç ayrı true/false alanı, kaç tane anlamsız durum yaratır? Davranışsal Kalıplar — State, Chain of Responsibility, Command · 9 dk

        5 kart sonraki derste seni bekliyor

        0/5 kart bu dersten toplandı