Sorumluluk Zinciri (Chain of Responsibility)

Bir kullanıcı(nesnel) isteğinin birden fazla nesne tarafından değerlendirilerek karşılanmaya çalışılmasına olanak sağlar. kullanıcı tek arayüz üzerinden isteğini iletir. İstek zincire bağlı nesneler tarafından sıra ile ele alınarak karşılanmaya calışılır. İstek karşılanana dek zincir üzerinde bir nesneden diğerine aktarılır. Zaman içinde zincire yeni nesneler eklenmesi ya da çıkarılması mümkündür. Kullanıcı bu tür değişikliklerden arayüz sayesinde etkilenmez.

Asım kredi karti aldigi gunden beri harcamalarini kontrol altina alamaz oldu. Sanki kredi kartiyla bedava alisveris yapiyormus gibi her pos cihazim var diyen kisiye kredi kartiyla kosuveriyor. Ay sonu ekstre gelincede yine kredi kartiyla onune cikan ilk eczaneye dalip en etkilisinden bir agri kesici alip susuz yutuyor. hem de pespese bir kac tane.

Asım ‘ın kredi kartindan dolayi cektigi iskenceler sadece bununla sinirlida degil ustelik.

Her yil uyelik aidati adi altinda ektreye ilave edilen 30 ytl den tutunda asgari ucreti odemek icin bankada bekledigi siraya kadar bir suru sikinti cekiyor. Uyelik aidati ile tuketici dernegine yaptigi basvurudan henuz bir ses cikmadi. Bankaya yazdigi dilekceye ise “aylik su kadar harcama limitini gecerseniz uyelik aidati almayiz” gibi bir cevap geldi. bi suru sovdu saydi ama uyelik aidatini pasa pasa odedi Asım.

Bir sure sonra ise bankamatikle kredi karti borcunu odeyebildigini ogrendi Asım. Sevinse mi uzulse mi bilemedi acikcasi. Kredi kartini bankamatige yerlestirdikten sonra sifresini girdi, borc ode dugmesine basti, bankamatigin alt bolumunden koca bir agiz acildi hiriltili bir ses cikararak ardindan Asım elindeki banknotlari o bolume koyacaktiki gerek kalmadi o kocaman agiz hiriltili sesler cikartarak daha paralari yariyolda Asım in elinden kaparak icine cekmis haril hurul paralari kontrol etmeye baslamisti bile. bir sure sonra ekranda su kadar para yatirdiniz diye bir uyari gelmis ve onay icin Asım ın bir tusa basmasini bekliyordu.

Asım bu duruma epey sasirmisti. Bankamatigin karisik bir sekilde kendisine verilen paralari dogru bir sekilde tanimlamasi garipti. Az bucuk programlama bilgisi vardi ve bunun nasil oldugunu biraz merak etmisti. Internette birazcik arastirmanin ardindan gerekli teknik bilgiye ulasmisti. Paralar once hava akimi sayesinde birbirinden ayristiriliyor ardindan banknotlarin bazi ozelliklerinden (agirlik, isigi gecirme orani, buyukluk vs.) yararlanilarak tanimlaniyordu. Hemen basit bir sekilde teknik detaylari bir tarafa birakarak bunun nasil programlanmis olabilecegini dusundu.

if BankNot5YTLmi(BanknotVerileri) Then
  Result :=‘5 YTL’
else
if BankNot10YTLmi(BanknotVerileri) Then
  Result :=‘10 YTL’
else
if BankNot20YTLmi(BanknotVerileri) Then
  Result :=‘20 YTL’
else
if BankNot50YTLmi(BanknotVerileri) Then
  Result :=‘50 YTL’
else
if BankNot100YTLmi(BanknotVerileri) Then
  Result :=‘100 YTL’
else
  Result :=‘tanimsiz’
end;

gibi birseyler geldi aklina. Sonra aklina Tasarim kaliplari geldi. Acaba boyle bir durum icin Tasarim kaliplarinda hazir bir cozum var miydi ?

Kaliplara soyle bir goz atarken CoR (Chain of Responsibility) kalibi dikkatini cekti. Bu kalibin cozum getirdigi temel sorun yukaridaki kodda goruldugu gibi birbirinin alternatifi cok olan durumlarda her bir alternatifi zincirin bir halkasi olarak sisteme yerlestirip anlasilmasi, okumasi, bakimi ve gelistirilmesi daha kolay bir yapi olusturmakti.

Bol bol case veya if then else kalibi kullanmak yerine bu kaliptaki her bir blogu bir nesne haline getirip zincire eklemek. Sonrada zincirin ilk halkasina gerekli verileri verip gerisine karismamak. Zincirin ilk halkasindaki nesne veriyi isleyip kendisiyle alakali olup olmadigina bakar, eger kendisiyle alakali bir veriyse gerekli islemleri yaparak sonucu geriye donderir eger kendisiyle alakali bir veri degilse ilgili veriyi zincirde kendisinden sonra gelen nesneye devrederek aradan cekilir. Bu surec bu sekilde zincirin sonuna kadar devam eder.

Asım hemen dusunmeye basladi. CoR kalibini banknot olayina uygulayabilir miydi acaba ?
Hemen 5 tane sinif tanimladi.

5lik
10luk
20lik
50lik
ve
100luk diye.

Bu siniflari zincirin birer halkasi gibi pespese bagladi.
5lik –> 10luk –> 20lik–> 50lik–>100luk seklinde

hemen ardindan elindeki banknotla ilgili olan verileri 5lik sinifina aktardi.Asım uzerine duseni yapmisti. bundan sonrasi artik CoR kalibinin isiydi. Veriyi tanimlamak ve sonucu Asım a dondurmek icin CoR kalibinin izleyecegi yol su sekilde olacakti. Once 5lik sinifi, eldeki verileri degerlendirip paranin 5 ytl olup olmadigina karar verecekti. eger para gercekten 5 ytl ise Asım a sonucu geri donderecek eger para 5 ytl degilse 5lik sinifi verileri kendisinden sonra gelen halkaya yani 10luk sinifina aktaracak. bu sefer 10luk sinifi, verileri kontrol edip paranin 10 ytl olup olmadigini kontrol edecek. eger 10 ytl ise sonucu Asım a donrecek eger para 10 ytl degilse ilgili verileri kendinden sonraki halkaya yani 20lik sinifina aktaracak. bu suruc bu sekilde zincirin sonuna kadar devam edecek.

Asım mutlu olmustu :)
Kurguladi bu sistem cok hosuna gitmisti. Sanki hersey otomatik oluyormus gibi bir hisse kapildi Asım. o sadece elindeki verileri zincirin ilk elemanina veriyor ve geriye donecek olan sonucu bekliyordu. Verileri islemek zincirdeki siniflarin sorunuydu. :)
Ustelik bu zincir isi de hosuna gitmisti. Zincire yeni bir halka eklemek ya da varolan bir halkayi zincirden cikartmak Asım icin cocuk oyuncagiydi. Ilerde yeni bir banknot ciktigi zaman ya da bir banknot tedavulden kalktigi zaman bunu kolaylikla sistemine uyarlayabilecekti.

Bu kadar mutluluk yeter de artardi Asım icin. hemen bir uygulama yapmaya karar verdi.
once nasil birsey tanimlamasi gerektigini dusundu.
Bir zincir kurabilmesi icin neler gerekliydi ?
Eskiden Linked List diye birsey gormustu Asım. Birbirini isaret eden Record lardan olusan bir yapiydi bu Linked Liste. Hemen aklina listedeki bir sonraki elemani isaret eden bir degisken kullandigi geldi aklina. Yeni yapacagi 5lik, 10luk siniflarinda boyle bir degiskene ihtiyaci vardi bi kere. bunu aklinin bir yerine not etti. Hemen ardindan bu siniflarin kendilerine verilen verileri kontrol edip karar verebilecekleri birer metodu olmasi gerektigini dusundu.

birbirine benzer 5 sinif olacagindan bu siniflarin ortak noktalarini icerecek soyut bir sinif tanimlayarak ise baslamanin daha dogru olacagini dusunurek oncelikle TAbstractBanknotController adli bir sinif tanimladi Asım.

TAbstractBanknotController = class
  Private
    fNextBanknotController:TAbstractBanknotController;
  public
    Constructor Create(aNextBanknotController: TAbstractBanknotController);
    function KontrolEt(aBanknot:String):String; Virtual;abstract;
  end;

Hemen TAbstractBanknotController sinifinin Create metodunu olusturarak zincirin bir sonraki halkasini nesnenin icerisinde ki bir degiskene atayarak sakladi.

constructor TAbstractBanknotController.Create(
  aNextBanknotController: TAbstractBanknotController);
begin
  inherited Create;
  fNextBanknotController := aNextBanknotController;
end;

KontrolEt metodu ise bu ata sinifta soyut (abstract) olarak tanimlandigindan ona el sur(e)medi. Gercek Banknot siniflarini tanimladiginda her sinif icin bu metodu yeniden yazmasi gerekecek Asım ın.

Soyut sinifi hazirladiktan sonra simdi bu siniftan tureterek olusturacagi asil siniflarina geldi sira. Ilk once 5lik banknotlari tanimlamakta kullanacagi sinifi yazmaya karar verdi Asım.

TBCBesYtl = Class(TAbstractBanknotController)
  public
    Function KontrolEt(aBanknot:String):String; override;
  end;

Sinifi tanimladiktan sonra KontrolEt metodunu su sekilde yazdi.

function TBCBesYtl.KontrolEt(aBanknot:String):string;
begin
  if aBanknot = ‘5′ then
  begin
    Result := aBanknot + ‘ degerinin 5 YTL olduğu tespit edildi’
  end
  else
    if fNextBanknotController (farkli) nil then
      Result :=  fNextBanknotController.KontrolEt(aBanknot)
     else
       Result := aBanknot + ‘ degeri tanımlanamadı’
end;

sonra diger siniflari tanimladi.

TBCOnYtl = Class(TAbstractBanknotController)
  public
    Function KontrolEt(aBanknot:String):String; override;
  end;

  TBCYirmiYtl = Class(TAbstractBanknotController)
  public
    Function KontrolEt(aBanknot:String):String; override;
  end;

  TBCElliYtl = Class(TAbstractBanknotController)
  public
    Function KontrolEt(aBanknot:String):String; override;
  end;

  TBCYuzYtl = Class(TAbstractBanknotController)
  public
    Function KontrolEt(aBanknot:String):String; override;
  end;

ve bu siniflarin KontrolEt metodlarini

function TBCOnYtl.KontrolEt(aBanknot: String):string;
begin
  if aBanknot = ‘10′ then
  begin
    Result := aBanknot + ‘ degerinin 10 YTL olduğu tespit edildi’
  end
  else
    if fNextBanknotController (farkli) nil then
      Result :=  fNextBanknotController.KontrolEt(aBanknot)
     else
       Result := aBanknot +  ‘ degeri tanımlanamadı’
end;

{ TBCYirmiYtl }

function TBCYirmiYtl.KontrolEt(aBanknot: String):string;
begin
  if aBanknot = ‘20′ then
  begin
    Result := aBanknot + ‘ degerinin 20 YTL olduğu tespit edildi’
  end
  else
    if fNextBanknotController (farkli) nil then
      Result :=  fNextBanknotController.KontrolEt(aBanknot)
     else
       Result := aBanknot + ‘ degeri tanımlanamadı’
end;

{ TBCElliYtl }

function TBCElliYtl.KontrolEt(aBanknot: String):string;
begin
  if aBanknot = ‘50′ then
  begin
    Result := aBanknot + ‘ degerinin 50 YTL olduğu tespit edildi’
  end
  else
    if fNextBanknotController (farkli) nil then
      Result :=  fNextBanknotController.KontrolEt(aBanknot)
     else
       Result := aBanknot + ‘ degeri tanımlanamadı’
end;

{ TBCYuzYtl }

function TBCYuzYtl.KontrolEt(aBanknot: String):string;
begin
  if aBanknot = ‘100′ then
  begin
    Result := aBanknot + ‘ degerinin 100 YTL olduğu tespit edildi’
  end
  else
    if fNextBanknotController (farkli) nil then
      Result :=  fNextBanknotController.KontrolEt(aBanknot)
     else
       Result := aBanknot + ‘ degeri tanımlanamadı’
end;

siniflari hazirladiktan sonra bunlari kullanmaya sira gelmisti. Asım bunun icin form uzerine iki adet TMemo bileseni koydu ve ortalarina da bir adet TButton koydu. Soldaki memoya

1
5
10
20
40
50
100

gibi degerler yazacak. ortadaki dugmeye bastiginda ise sol daki memoda yazan rakamlari teker teker zincirin ilk halkasina vererek sonuclari sagdaki memoda gorecekti.

cor.jpg

butonun kodlarini ise su sekilde yazdi Asım.

procedure TForm1.Button1Click(Sender: TObject);
var
  BanknotBeslik,BanknotOnluk,
  BanknotYirmilik,BanknotEllilik,
  BanknotYuzluk:TAbstractBanknotController;
  i:Integer;
begin

BanknotYuzluk   := TBCYuzYtl.Create(nil);
BanknotEllilik  := TBCElliYtl.Create(BanknotYuzluk);
BanknotYirmilik := TBCYirmiYtl.Create(BanknotEllilik);
BanknotOnluk    := TBCOnYtl.Create(BanknotYirmilik);
BanknotBeslik   := TBCBesYtl.Create(BanknotOnluk);

memo2.Lines.Clear;

for i:=0 to Memo1.Lines.Count - 1 do
 memo2.Lines.Add(BanknotBeslik.KontrolEt(Memo1.Lines[i]));

//Banknotlari Free eden kodlar…

end;

Asım, hayatin kotu yanlarindan da birseyler ogrenebildigine sevindi. Bu arada Asım ın bir kac gun once kredi kartlarini iptal ettirmek icin subeye basvurdugunu biliyorum. Kim bilir belki bu surec icerisinde hayatinda baska bir tasarim kalibi oldugunu kesfeder ve yine burada bizimle paylasir ;)

2 Responses to “Sorumluluk Zinciri (Chain of Responsibility)”


  1. 1 Evren

    Harika bir makele/yazı olmuş ellerine sağlık. Bu objelerin tamamını TBanknotTani gibi bir main objenin içinden yönetmek sanırım hiyerarşi ve kullanım açısından daha faydalı olacaktır.

    Birde merak ettim yazı bir çeviri uyarlaması mı yoksa kendi eserin mi?

  2. 2 sadettinpolat

    @evren, yorum icin tesekkur ederim. TBanknotTani ya da TBanknotFactory adli bir sinifla Banknot siniflarinin olusturulmasi ve zincir yapisinin kurulmasi saglanabilirdi(aslinda saglanmalida) ama bu sefer olayin icerisine Factory kalibida gireceginden karisiklik olmasin diye bu kismi es gectim.

    bu yaziyi yazmadan once CoR kalibiyla ilgili turkce, ingilizce birkac yaziya goz attim. onlarin verdikleri ornekleri inceledim. once GoF tasarim kaliplarini delphiye uyarlayan Felix colibri nin ornegini birebir yazmaya niyetlendim ama ornek anlasilmasi biraz guc gelince boyle bir ornek uydurdum. Asım ı da bu ise alet ettim :)

Leave a Reply