Image
Ağustos 16 2015 02:28

Chain of Responsibility (Sorumluluk Zinciri)

 

                Concreate sınıflar, yani bu zincirin her bir halkasına denk gelen ve işlem yapan sınıflar aynı interfaceden türetilirler. Bu interfacelerde yapılacak iş ve bir sonraki basamak, zincirin bir sonraki halkasının bilgisi mevcuttur. 

Böylece ilgili sınıf kendisi ile ilgili işlemi tamamladıkdan sonra kendinden sonraki halkaya/basamağa işi devreder… taki zincirin son halkası da işini tamamlayana kadar.

Dolayısı ile her Concreate (somut/işi yapan) sınıflar birlerine Loosly Coupled (zayıf bağlarla) bağlıdırlar. Zincirden bir halkanın çıkarılması yada yeni bir tane eklenmesi durumlarında sadece önceki halka bilgisinde değişiklik yapılıar. Ancak hangi basamakta ne iş yapıldığı nasıl bir business olduğu hakkında bilgi sahibi olmalarına gerek yoktur.

Basamakta her zincir kendinden önceki kimin olduğu ile ilgilenmez sadece ne yapacağını ve kendinden sonra işi kime devredeceğini bilir.

Nerelerde kullanılabilir:  Workflow süreçlerinin olduğu yapılarda kullanılır. Wizardlar iyi örnek olabilir, Onaylama sistemleri, Önce ilk amir onaylasın bir üst amire göndersin o da imzalarsa sonraki amire iptal ederse ik müdürüne gibi. Loglama işlemlerinde önce şu loga yaz sonra buna onu yazabilrsen sadece bu loga yaz.
Yada bizim Repzone  de kullandığımız gibi , Cloud sisteminden bir üyelik başatıldığında, Önce Hesap bilgilerini oluştur, deneme sürümü için payment/subscription işlemlerini yap, üyeye email gönder, Crm de müşteri ilişkileri ile bağlantısını oluştur vs…

 chain of responsibility  

 

Diyagramda gördüğümüz bu bu tasarım kalbı üç temel yapıdan oluşuyor

Handler : İşi yapacak olan sınıfların uygulaması gereken Abstract yapı(Abstract yada interface) dolayısı ile bu sınıfı uygulayan bütün iş yapan concreate sınıflar bu sınıfta Process() ve NaxtHandler() (sonraki halka) property ve metodları uygulamak zorundadırlar.

Concreate : Asıl işi yapacak olan sınıflardır yani zincirin halkalarına tekabül eden sınıflardır. Herbir halka için bir concreate sınıf türetilir.

Client : Concreate sınıflardan iş yapmalarını isteyen ve zincirin ilk halkasını tetikleyen sınıflardır.

 

 chain of responsibility  

 

 Şimdi bizde basit bir para bozma Atm uygulaması yazalım.

Senaryo : Atm makinemizde 50,20,10 ve 1 TL lik sınırsız kaynağımız olsun. Para çekmek isteyen kişi bizden bir meblağ istesin bizde ona mümkün olduğunda en büyük para ile ödeme yapmaya çalışan bir sistem yazalım.

Bütün iş yapacak(Concreate) sınıfların türetileceği Handler sınıfımız olacak ve bu sınıftan türeyen 50,20,10 ve 1 tl için iş yapacak ConcreateClass larımız olacak

 public abstract class HandlerClass
    {
        public int Val { get; set; }
        public void Exchange (int amount)
        {
            if (amount >= Val)
            {
                var piece = amount / Val;
                var remaining = amount % Val;
                Console.WriteLine("{0} tane {1}'lik", piece, Val);
                if (remaining != 0)
                {
                    NextConcreateClass.Exchange(remaining);
                }
            }
            else
            {
                NextConcreateClass.Exchange(amount);
            }
        }
       
 public HandlerClass NextConcreateClass { get; set; }
    }

burada eğer istenen tutar şu anda iş yapan halkanın verebileceği tutara tam olarak denk gelirse bir kerede bütün tutarı ödeyip çıkıyor. küsürat kalıyorsa  kendisinden sonrakine iş ve kalan miktarı devrediyor.  

  public class ConcreateClass50 : HandlerClass
    {
        public ConcreateClass50()
        {
            Val = 50;
        }
    }




    public class ConcreateClass20 : HandlerClass
    {
        public ConcreateClass20()
        {
            Val = 20;
        }
    }

    public class ConcreateClass10 : HandlerClass     {         public ConcreateClass10()         {             Val = 10;         }     }     public class ConcreateClass1 : HandlerClass     {         public ConcreateClass1()         {             Val = 1;         }     }

böylece elimizde hem işin nasıl yapılacağını belirten bir abstract birde işi yapacak sınıflarımız olmuş oldu. Artık örneğimize çalıştırmak için client ekleyebiliriz. bu client te önce zinciri tanımlayacağız ardından zincirin ilk halkasına bir değer verip çalıştıracağız

 

class Program
    {
        static void Main(string[] args)
        {
            var concreateClass50 = new ConcreateClass50();
            var concreateClass20 = new ConcreateClass20();
            var concreateClass10 = new ConcreateClass10();
            var concreateClass1 = new ConcreateClass1();
 
            concreateClass50.NextConcreateClass = concreateClass20;
            concreateClass20.NextConcreateClass = concreateClass10;
            concreateClass10.NextConcreateClass = concreateClass1;
            concreateClass50.Exchange(199);
        }
    }


önce zincir tanımlandı, girilen tutar eğer 50 ye tam olarak bölünmüyorsa sonraki 20 ye gönderilecek,  20 ye de tam bölünmüyorsa 10 a gönderilecek ve en nihayetinde 10 da bölünmüyorsa 1 e gönderilecektir.
 
örnekte ben 199 girdim bu durumda nasıl bir çıktıda

3 adet 50 , 1 tane 20 lik , 1 tane 10 luk ve 9 tane 1 lik olmasını bekliyorum

 çıktı :

 chain of responsibility  

not :

Bu örneğimizde Exchange metodu bütün concreate sınıflarda aynı olduğu için Template Pattern kullanarak işlemi Abstract base sınıfımızda yaptık.

yine bütün sınıflar aynı işlemi (para bozma) yaptıkları ve sadece para aldıkları için aslında farklı bir yapı da kullanılabilirdi ama buradaki amacımız  Chain of Responsibility ile ilgili örnek yapmak olduğu için değinmiyoruz.

 

 

2015 : memet tayanç