Behavioral — Davranışsal Desenler
Chain of Responsibility
Sorumluluk Zinciri Deseni
Amaç: Bir isteği, onu işleyebilecek nesne bulunana kadar bir zincir boyunca iletmek; isteği gönderen ile işleyeni birbirinden ayırmak.
Basit Anlatım
Teknik terim yok — günlük hayattan bir benzetme
Bir şirkette harcama onayı istiyorsunuz. Kural şöyle: 1.000 TL'ye kadar olan harcamaları takım lideri, 10.000 TL'ye kadar müdür, 100.000 TL'ye kadar direktör onaylar. Daha fazlası için genel müdür gerekir.
Siz talebinizi her zaman takım liderine verirsiniz. Takım lideri yetkisi yetiyorsa onaylar; yetmiyorsa talebi müdüre iletir. Müdür de aynısını yapar. Talep, onu karşılayabilecek kişiye ulaşana kadar yukarı doğru ilerler.
Siz kimin onaylayacağını bilmek zorunda değilsiniz; sadece zincirin ilk halkasına verirsiniz.
Müşteri hizmetleri
Bir çağrı merkezini aradığınızda önce sesli yanıt sistemi karşılar. Çözemezse birinci seviye destek, o da çözemezse uzman ekip devreye girer.
Teknik Anlamı
Bir yazılımcının bakış açısıyla
Chain of Responsibility, isteği işleyebilecek nesneleri (handler) bir zincir halinde bağlar. Her handler isteği ya işler ya da zincirdeki bir sonraki handler'a iletir. Bazı varyasyonlarda her handler işini yapıp yine de isteği iletir (pipeline).
Katılımcılar
- Handler: İsteği işleme metodunu ve bir sonraki handler'a referansı tanımlar.
- ConcreteHandler: İsteği işleyebiliyorsa işler; değilse sonrakine iletir.
- Client: İsteği zincirin ilk halkasına gönderir.
.NET'ten tanıdık örnekler
- ASP.NET Core middleware pipeline: her middleware
next()çağırarak isteği sonrakine iletir ya da kısa devre yaparak yanıt döner. HttpClient'takiDelegatingHandlerzinciri.- MediatR'daki
IPipelineBehavior'lar (doğrulama, loglama, transaction).
Avantajlar
- Gönderen ile alıcı arasındaki bağ kopar: gönderen kimin işleyeceğini bilmez.
- Zincirin sırası ve halkaları çalışma zamanında değiştirilebilir.
- Her handler tek bir kurala odaklanır (Single Responsibility).
Dezavantajlar
- İsteğin hiçbir handler tarafından işlenmeme ihtimali vardır; zincirin sonunda varsayılan bir davranış düşünülmelidir.
- Uzun zincirlerde hata ayıklamak zorlaşabilir.
C# ile Uygulama
Adım adım, çalışan kod örneği
Senaryo: Yukarıda anlatılan harcama onay süreci.
Adım 1 İstek modeli ve soyut Handler
SetNext handler'ı döndürdüğü için zincir akıcı biçimde kurulabilir.
public record ExpenseRequest(string Employee, decimal Amount, string Purpose);
public abstract class Approver
{
private Approver? _next;
public Approver SetNext(Approver next)
{
_next = next;
return next;
}
public void Handle(ExpenseRequest request)
{
if (CanApprove(request.Amount))
Console.WriteLine($" {GetType().Name,-14} ONAYLADI: " +
$"{request.Purpose} ({request.Amount:N0} TL)");
else if (_next is not null)
{
Console.WriteLine($" {GetType().Name,-14} yetkisi yetmiyor, iletiliyor...");
_next.Handle(request);
}
else
Console.WriteLine($" REDDEDİLDİ: {request.Amount:N0} TL kimsenin yetkisinde değil.");
}
protected abstract bool CanApprove(decimal amount);
}Adım 2 Somut Handler'lar
Her biri yalnızca kendi yetki sınırını bilir.
public class TeamLead : Approver
{
protected override bool CanApprove(decimal amount) => amount <= 1_000;
}
public class Manager : Approver
{
protected override bool CanApprove(decimal amount) => amount <= 10_000;
}
public class Director : Approver
{
protected override bool CanApprove(decimal amount) => amount <= 100_000;
}
public class CEO : Approver
{
protected override bool CanApprove(decimal amount) => amount <= 1_000_000;
}Adım 3 Kullanım
Tüm parçaları bir araya getiren istemci (client) kodu:
// Zinciri kur: TeamLead -> Manager -> Director -> CEO
var chain = new TeamLead();
chain.SetNext(new Manager()).SetNext(new Director()).SetNext(new CEO());
var requests = new[]
{
new ExpenseRequest("Ayşe", 450, "Ofis malzemesi"),
new ExpenseRequest("Can", 7_500, "Eğitim semineri"),
new ExpenseRequest("Elif", 250_000, "Yeni sunucular"),
new ExpenseRequest("Mert", 5_000_000, "Yeni bina"),
};
foreach (var r in requests)
{
Console.WriteLine($"{r.Employee} -> {r.Amount:N0} TL talep etti:");
chain.Handle(r);
}Konsol çıktısı
Ayşe -> 450 TL talep etti: TeamLead ONAYLADI: Ofis malzemesi (450 TL) Can -> 7.500 TL talep etti: TeamLead yetkisi yetmiyor, iletiliyor... Manager ONAYLADI: Eğitim semineri (7.500 TL) Elif -> 250.000 TL talep etti: TeamLead yetkisi yetmiyor, iletiliyor... Manager yetkisi yetmiyor, iletiliyor... Director yetkisi yetmiyor, iletiliyor... CEO ONAYLADI: Yeni sunucular (250.000 TL) Mert -> 5.000.000 TL talep etti: TeamLead yetkisi yetmiyor, iletiliyor... Manager yetkisi yetmiyor, iletiliyor... Director yetkisi yetmiyor, iletiliyor... REDDEDİLDİ: 5.000.000 TL kimsenin yetkisinde değil.
Kodda dikkat edilmesi gerekenler
- İstemci yalnızca zincirin ilk halkasını (
chain) biliyor. - Yeni bir onay seviyesi (ör. "Bölge Müdürü") eklemek için yeni bir sınıf yazıp zincire eklemek yeterli; diğer sınıflar değişmez.
- Zincirin sonunda kimse onaylayamazsa varsayılan davranış (red) devreye giriyor.