İçeriğe geç

Davranışsal Kalıplar — State, Chain of Responsibility, Command

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

Önce şunu oku: Tasarım Kalıpları — Ne Zaman Kazandırır

30 saniyede özet

Bir nesnenin davranışı durumuna göre değişiyorsa durumu açıkça say; üç ayrı true/false, anlamsız karışımlara izin verir. Sıralı kontroller için zincir kur, sonraya bırakılacak isteği bir nesneye çevir.

Bir sipariş hem iptal edilmiş hem kargoda. Müşteri parasını geri aldı, paket de kapısına geldi. Kodda yanlış bir satır yok; sadece bir kontrol eksik.

  1. Bayt: Bir sipariş hem iptal edilmiş hem kargoda! Müşteri hem parasını aldı hem paketini.

  2. Sen: Kodda bir hata mı var?

  3. Bayt: Yanlış satır yok. Sadece üç ayrı düğme, olmaması gereken bir karışıma izin veriyor.

  4. Bayt: Kaç karışım mümkün, kaçı anlamlı? Önce sen say.

State: durumu açıkça say

Sipariş isPaid, isShipped, isCancelled boolean'larıyla tutuluyor. Kaç farklı kombinasyon mümkün ve kaçı anlamlı? Cevabı göster

Sekiz kombinasyon, ama anlamlı olan yalnızca birkaçı. “Kargoda ama ödenmemiş” ya da “iptal edilmiş ama kargoda” da kodun izin verdiği durumlardır.

Üç ayrı düğme mi, tek bir döner anahtar mı?
Adım adım oku
  1. Sipariş durumu üç ayrı boolean ile tutuluyor: ödendi, kargoda, iptal. Birlikte sekiz karışım mümkün.
  2. Kargoda ve iptal düğmeleri aynı anda açılabiliyor: iptal edilmiş ama kargoda bir sipariş.
  3. Durum tek bir açık tipe taşınınca, yani tek bir anahtar olunca, her pozisyonun adı var.
  4. Anahtar aynı anda yalnızca bir pozisyonda durur. Anlamsız bir durum artık yazılamaz.

StateDavranış duruma göre değişiyorsa durumu açık bir tip olarak tutup her durumda izin verilen işlemleri tanımlamak. Geçersiz kombinasyonlar ifade edilemez olur.Sözlükte gör → kalıbı durumu tek bir alan olarak tutar ve her durumda hangi işlemin izinli olduğunu tanımlar. Geçersiz kombinasyonlar ifade edilemez hâle gelir.

Kafam karıştı, daha basit anlat

Üç ayrı evet/hayır kutucuğu, “iptal edildi ama kargoda” gibi saçma durumlara izin verir. Durumu tek bir alanda tutarsan, sipariş aynı anda yalnızca tek bir yerde olabilir.

Hızlı kontrolBaşlangıç

Bir siparişin durumunu `isPaid`, `isShipped`, `isCancelled` boolean'larıyla tutmanın temel sorunu nedir?

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

Bir durum makinesinde geçersiz bir geçiş istendiğinde (SHIPPED durumundaki siparişe pay()) en iyi davranış hangisi?

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

Kendin gör

Sipariş durumu — boolean'lar mı, açık bir durum makinesi mi?

Tohum 692627
  • isPaid = false
  • isShipped = false
  • isDelivered = false
  • isCancelled = false
  1. Oynat ya da adımla.
Hız
Adım 0

Şu an ne oldu?

Senaryo: pay() → ship() → deliver()

Üç boolean, sekiz olası kombinasyon.

Görevler0/3

  • Müşteriden iki kez para çekaçık

    İpucu

    Ödeme düğmesine iki kez basılırsa pay() bunu fark ediyor mu?

  • Hem kargoda hem iade edilmiş bir sipariş üretaçık

    İpucu

    cancel() teslimatı kontrol ediyor. Kargoyu?

  • Aynı hatalı senaryoyu geçersiz geçişi reddederek bitiraçık

    İpucu

    Mutlu yol dışındaki bir senaryoyu, izinli geçiş tablosu olan uygulamayla dene.

Olay günlüğü (0)

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

  1. Varsayılanla oynat. Mutlu yolda iki uygulama da aynı sonucu veriyor.
  2. “Öde → öde” seç. Boolean’lı sürüm kartı iki kez çekiyor.
  3. “Öde → kargola → iptal et” seç. Hem kargoda hem iade edilmiş bir sipariş.
  4. Uygulamayı “State” yap. Aynı senaryolar, geçersiz adım olduğu yerde reddediliyor.
Kafam karıştı, daha basit anlat

Her durum hangi düğmelerin basılabileceğini bilir. Kargodaki bir siparişte “iptal” düğmesi hiç çalışmaz.

Hızlı kontrolOrta

Kargodaki siparişler iptal edilip paraları iade ediliyor, paketler yine de müşteriye gidiyor. Hangi satır?

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

Hatalı satıra dokun, sonra kontrol et.

Order.java
Java 21UTF-8LF

Satır satır: enum ile durum makinesi

Her durum için açık bir karar

Order.java
1public void cancel() {
şu an çalışan satır status = switch (status) {
3 case NEW -> CANCELLED;
4 case PAID -> {
5 payments.refund(id);
6 yield CANCELLED;
7 }
8 case SHIPPED, DELIVERED, CANCELLED ->
9 throw new IllegalStateException(status + " durumunda iptal edilemez");
10 };
11}

Debug

Adım 1/4

derleyici Switch bir ifade: bütün enum değerlerini kapsamak zorunda. default yok.

Java 21UTF-8LF2:1

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

Default dalı yazma. Yeni bir durum eklendiğinde ele alınmayan her yeri derleyici gösterir.

Hızlı kontrolOrta

Durum makinesini Java'da enum ve switch ifadesiyle yazmanın, if-else zincirine göre asıl kazancı ne?

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

Zincir kur, isteği nesneye çevir

KalıpFikirTanıdık örnek
Chain of ResponsibilityBir isteği sıralı işleyicilerden geçirmek: her biri ya isteği karşılar ya da bir sonrakine iletir. Servlet filtreleri ve Spring Security filtre zinciri tipik örnek.Sözlükte gör →Her halka ya karşılar ya sonrakine iletirServlet filtreleri, Spring Security filtre zinciri
CommandBir isteği, çalıştırılacak bir nesne olarak paketlemek. Böylece kuyruğa koyulabilir, ertelenebilir, yeniden denenebilir, loglanabilir ya da geri alınabilir.Sözlükte gör →İstek, çalıştırılacak bir nesnedirRunnable, kuyruktaki iş, geri al

Zincirde sıra bir karardır. Rate limit kimlik doğrulamadan önce mi çalışmalı, sonra mı? Cevap sistemin davranışını değiştirir.

Command’ın değeri istek hemen çalışmadığında ortaya çıkar: kuyruğa koymak, yeniden denemek, kaydetmek, geri almak.

Bir FAST transferi gönderilmeden önce birkaç kontrolden geçer. Bu kontrolleri küçük bir zincir olarak kurunca her halka kısa kalıyor, sıra da tek bir yerde görünüyor.

Derinleş · FAST transferi: kontrol zinciri 4 dosya · ~68 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/java/com/bank/transfer/check/ TransferCheck.java Her halka ya karar verir ya da isteği bir sonrakine iletir.

src/main/java/com/bank/transfer/check/TransferCheck.java
// One link: decide, or hand the request to the rest of the chain.
public interface TransferCheck {
Decision check(TransferRequest request, TransferCheckChain next);
sealed interface Decision {
record Approved() implements Decision {}
record Rejected(String reason) implements Decision {}
}
}

src/main/java/com/bank/transfer/check/ DailyLimitCheck.java Günlük limit halkası: limit aşılırsa reddeder, aşılmazsa zinciri sürdürür.

src/main/java/com/bank/transfer/check/DailyLimitCheck.java
@Component
class DailyLimitCheck implements TransferCheck {
private static final Currency TRY = Currency.getInstance("TRY");
private final DailyUsage usage;
private final Money dailyLimit;
DailyLimitCheck(DailyUsage usage, @Value("${transfers.fast.daily-limit-try}") BigDecimal limit) {
this.usage = usage;
this.dailyLimit = new Money(limit, TRY); // FAST moves TRY only
}
@Override
public Decision check(TransferRequest request, TransferCheckChain next) {
Money afterThis = usage.sentToday(request.customerNo()).plus(request.amount());
if (afterThis.isGreaterThan(dailyLimit)) {
return new Decision.Rejected("daily FAST limit exceeded");
}
return next.proceed(request);
}
}

src/main/java/com/bank/transfer/check/ TransferCheckChain.java Zincirin sonu açık: bütün halkalardan geçen istek onaylanır, asla null dönmez.

src/main/java/com/bank/transfer/check/TransferCheckChain.java
// Immutable: every link receives a chain that starts one step further on.
public final class TransferCheckChain {
private final List<TransferCheck> checks;
private final int position;
public TransferCheckChain(List<TransferCheck> checks) {
this(List.copyOf(checks), 0);
}
private TransferCheckChain(List<TransferCheck> checks, int position) {
this.checks = checks;
this.position = position;
}
public TransferCheck.Decision proceed(TransferRequest request) {
if (position == checks.size()) {
// The end is explicit: a request that passed every link is approved.
return new TransferCheck.Decision.Approved();
}
return checks.get(position).check(request, new TransferCheckChain(checks, position + 1));
}
}

src/main/java/com/bank/transfer/check/ TransferChecksConfig.java Sıra tek bir yerde yazılı: yaptırım taraması en önde.

src/main/java/com/bank/transfer/check/TransferChecksConfig.java
@Configuration
class TransferChecksConfig {
// The order is a decision, written down once. Sanctions screening goes first:
// a listed beneficiary must be caught and reported whatever the amount,
// not turned away early as "over the limit".
@Bean
TransferCheckChain fastTransferChecks(SanctionsScreeningCheck sanctions,
DailyLimitCheck dailyLimit,
FraudScoreCheck fraud) {
return new TransferCheckChain(List.of(sanctions, dailyLimit, fraud));
}
}
Kafam karıştı, daha basit anlat

Zincir, bir isteğin sırayla geçtiği kontrol noktalarıdır. Command ise isteği bir kâğıda yazmaktır: kâğıdı kuyruğa koyabilir, sonra yeniden deneyebilirsin.

Hızlı kontrolOrta

Spring Security'nin filtre zinciri hangi davranışsal kalıbın örneğidir?

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

Her ihtiyacı en uygun davranışsal kalıpla eşleştir.

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

Sınıflandırılmamış

State

Davranış nesnenin durumuna göre değişiyor

    Chain of Responsibility

    İstek sıralı işleyicilerden geçiyor

      Command

      İstek bir nesne olarak saklanıyor ya da erteleniyor

        Command kalıbının asıl değeri ne zaman ortaya çıkar?

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

        Tuzaklar

        İki durum için durum makinesi. Aktif/pasif için bir boolean yeter; kalıp üçüncü durumla gelir.

        Kuralları servislere dağıtmak. Geçiş kuralı entity’nin kendi metodunda durmalı; beş serviste beş farklı kontrol olmamalı.

        Sonu tanımsız zincir. Hiçbir halka karşılamazsa null dönen zincir, hatayı uzak bir yere taşır.

        Eşzamanlı geçişler. Bellekteki durum makinesi doğru olsa da iki istek aynı anda geçiş yapabilir; veritabanında @Version gerekir.

        Kendini sına

        Şimşek turu1/5

        Üç boolean alan birlikte sekiz farklı kombinasyon üretir.

        Soru 1/2İleri

        İki kullanıcı aynı anda aynı siparişi hem kargoya veriyor hem iptal ediyor. Nesnedeki durum makinesi doğru olsa bile ne ek gerekir?

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

        Aklında kalacak üç şey

        1. 1 Üç boolean sekiz ihtimal üretir ve çoğu anlamsızdır. Durumu tek bir açık tip olarak tutmak geçersiz olanları yazılamaz kılar.
        2. 2 Chain of Responsibility'de her halka ya işi yapar ya bir sonrakine iletir. Sıra ve 'kimse yapmazsa ne olur?' sorusu açıkça cevaplanmalıdır.
        3. 3 Command isteği bir nesneye çevirir; böylece kuyruğa konabilir, yeniden denenebilir ya da geri alınabilir.
        Sonraki kapı Aynı kodu iki kez yazmak her zaman kötü müdür? DRY, KISS, YAGNI ve Demeter — Değişimi Ucuz Tutmak · 10 dk

        5 kart sonraki derste seni bekliyor

        0/5 kart bu dersten toplandı