Chain of Responsibility Design Pattern (Sorumluluk zinciri modeli), müşteriden gelen bir talebin bunları işlemek için bir nesneler zincirine iletildiği yazılım tasarımında gevşek bağlantı (loose coupling) elde etmek için kullanılır. Daha sonra zincirdeki nesne, talebi kimin işleyeceğine ve talebin zincirdeki bir sonraki nesneye gönderilmesi gerekip gerekmediğine kendisi karar verecektir.
Bu tasarımda ki zincir benzetmesi, sorumluluğu üstlenecek olan nesnelerin belirli bir sırayla ve duruma göre, görevi bir sonraki nesneye iletmesinden gelir. İstemcinin bilgisi dışında yani bağımsız bir şekilde zincire yeni nesneler eklenebilir ya da çıkartılabilir.
Bu tasarım modeli genellikle istemcinin içsel olarak nasıl yapıldığını bilmediği bir işi yaptırmak üzere kapalı bir sisteme talep göndermesi ve bu sistemdeki nesnelerin belirli bir sıra düzen içerisinde bu isteği handle etmesinin gerektiği durumlarda kullanılır.
UML Class Diyagramı
Chain of Responsibilities Prensipleri
- Handler: İstekleri işlemek için bir arayüz tanımlar.
- ConcreteHandler: Sorumlu olduğu istekleri ele alır ve isteği işleyebilirse, bunu yapar; aksi takdirde talebi halefine iletir.
- Client: Zincirdeki bir ConcreteHandler nesnesine istek başlatır.
C# Chain of Responsibilities Design Pattern Uygulama
Bu modeli uygulamak için ATM makinasi örneği geliştireceğiz. Kullanıcı çekmek istediği miktarı ATM’ye girdiğinde ATM ödemeyi önceden tanımlı olan 10TL, 20TL, 50TL cinslerinden gerçekleştirecektir. Girilen miktar doğrultusunda elinde ki cinslerden dağıtım yapacaktır. 10’un katı olamayan bir değer girilirse işlemi gerçekleştiremeyecektir.
Dağıtım zinciri şu şekilde gerçekleşecektir: 50 – – > 20 – – > 10
Chain uygulamaları tarafından dağıtılacak ve kullanılacak miktarı depolayacak bir Para Birimi sınıfı oluşturyoruz.
public class Currency
{
private int amount;
public Currency(int amt)
{
this.amount = amt;
}
public int getAmount()
{
return this.amount;
}
}
Temel arabirim, zincirdeki (chain) bir sonraki işlemciyi ve isteği işleyecek metodu tanımlayacak bir metoda sahip olmalıdır. Dispense interface’i aşağıdaki gibi görünecektir.
public interface IDispenseChain
{
void setNextChain(IDispenseChain nextChain);
void dispense(Currency cur);
}
DispenseChain interface’ini uygulayacak ve dispense metotlarının uygulanmasını sağlayacak farklı işlemci sınıfları oluşturmamız gerekiyor. Sistemimizi 50TL, 20TL ve 10TL olmak üzere üç tür banknotla çalışacak şekilde geliştirdiğimiz için, üç somut uygulama oluşturacağız.
public class TL50Dispenser : IDispenseChain
{
private IDispenseChain chain;
public void dispense(Currency cur)
{
if (cur.getAmount() >= 50)
{
int num = cur.getAmount() / 50;
int remainder = cur.getAmount() % 50;
Console.WriteLine(num + " adet 50TL verildi.");
if (remainder != 0) this.chain.dispense(new Currency(remainder));
}
else
{
this.chain.dispense(cur);
}
}
public void setNextChain(IDispenseChain nextChain)
{
this.chain = nextChain;
}
}
public class TL20Dispenser : IDispenseChain
{
private IDispenseChain chain;
public void dispense(Currency cur)
{
if (cur.getAmount() >= 20)
{
int num = cur.getAmount() / 20;
int remainder = cur.getAmount() % 20;
Console.WriteLine(num + " adet 20TL verildi.");
if (remainder != 0) this.chain.dispense(new Currency(remainder));
}
else
{
this.chain.dispense(cur);
}
}
public void setNextChain(IDispenseChain nextChain)
{
this.chain = nextChain;
}
}
public class TL10Dispenser : IDispenseChain
{
private IDispenseChain chain;
public void dispense(Currency cur)
{
if (cur.getAmount() >= 10)
{
int num = cur.getAmount() / 10;
int remainder = cur.getAmount() % 10;
Console.WriteLine(num + " adet 10TL verildi.");
if (remainder != 0) this.chain.dispense(new Currency(remainder));
}
else
{
this.chain.dispense(cur);
}
}
public void setNextChain(IDispenseChain nextChain)
{
this.chain = nextChain;
}
}
Burada dikkat edilmesi gereken önemli nokta, dağıtma yönteminin uygulanmasıdır. Her uygulamanın isteği işlemeye çalıştığını ve miktara bağlı olarak talebin bir kısmını veya tamamını işleyebileceğini fark edeceksiniz. Zincirlerden biri tam olarak işleyemezse, kalan isteği işlemek için zincirdeki bir sonraki işlemciye istek gönderir. İşlemci herhangi bir şeyi işleyemezse, aynı talebi bir sonraki zincire iletir.
Burası önemli bir adım ve zinciri dikkatli bir şekilde oluşturmalıyız, aksi takdirde bir işlemci hiç istek alamayabilir.
İşte kullanıcının talep ettiği miktarı işlemek için ATM Dispenser uygulamamız.
public class ATMDispenseChain
{
public IDispenseChain c1;
public ATMDispenseChain()
{
// initialize the chain
this.c1 = new TL50Dispenser();
IDispenseChain c2 = new TL20Dispenser();
IDispenseChain c3 = new TL10Dispenser();
// set the chain of responsibility
c1.setNextChain(c2);
c2.setNextChain(c3);
}
}
Program.cs – Main Method
ATMDispenseChain atmeDispenser = new ATMDispenseChain();
while (true)
{
int amount = 0;
Console.WriteLine("Çekmek istediğiniz miktar: ");
amount = Convert.ToInt32(Console.ReadLine());
if (amount % 10 != 0)
{
Console.WriteLine("Tutar 10'unun katı olmalıdır.");
return;
}
atmeDispenser.c1.dispense(new Currency(amount));
}
Sonuç
Çekmek istediğiniz miktar:
330
6 adet 50TL verildi.
1 adet 20TL verildi.
1 adet 10TL verildi.