Image
Haziran 21 2015 02:28

Command Pattern

"Problem yoksa, tasarım pattern yoktur" altın kuralını bir daha hatırlatarak "Command Pattern" tasarımına başlamak istiyorum.

Ne zaman uygulanmalı? 

Benim bir Execution işlemim var, yani bir metod çağırma işlemim var 

-Bu metod hakkında bilgi almak isteyebilirim 
-Bu işlemi saklamak isteyebilirim(History,log) 
-Bu işlemin Turşusunu kurmak isteyebilirim 
-Bu işlemi Undo yapmak isteyebilirim 
-Bu işlemi Yeniden çalıştırmak isteyebilirim 
-Karşımdaki kişi Bu işlemi çağırdığını düşünsün ama ben müsait olunca o işi yaparım 
-Bir sürü metod çağrılıyor, dolayısı ile yapılması beklenen iş listesi var belki bunları sıraya sokmak isteyebilirim 

vb metod çağırma işlemlerine müdahil olmak istediğinizde artık o metodu nesne haline getirip saklamak için kullanılabilir.

            Diyelimki client bizden bir dosya silmemizi istedi, bu durumda ne yapmamız gerekiyor?  bütün threadleri suspend edip dosyayı siliyorum siz bekleyin deyip clienti bekletebilir miyiz? Sanmıyorum 
Yada bir anda herkes dosya silme işlemi yaptı onlarca silme işlemi geldi. Hepsine cevap veremeyiz ancak o emri yerine de getirmemiz lazım. Bir yandan da koasa da izin veremeyiz. Belki client dosya  silirken bir sürü parametre vs ilede çalıştırıyordur. 

İşte bu yüzden bu Execution işlemlerini nesne haline saklayıp bir şekilde Invoke etmemiz gerekiyor.
 a- Bütün bunları yaparkende Cliente "Tamam ben o dosyayı sileceğim sen beni bekleme çalışmaya devam et" diyeceğiz. 
 b- İşi bitirdiğimde de cliente "Senin bu işin bitti" diyebilirim 
 c - Client benim bekleyen ne işlerim var dediği zaman "Senin bekleyen şu işlerin var " diye liste verebiliriz 

Aklıma gelmişken Unit Of Work te nasıl çalışıyoruz, OnuYap(),BunuYap(),SunuSil() vs..  diyoruz peki hemen databaseden siliniyor mu? o işler listeleniyor taki biz SaveChanges() deyinceye kadar ancak ondan sonra gerçekten işlem yapıyor. 

İşin özü : Yapılan işlemlerin nesne olarak karşılıklarını saklama işidir. Bizim bu işleme ihtiyaç duyduğumuz yerlerde bu tasarımdan istifade edebiliriz. 

Blog image

 

Command: Execution işlemini barındıran abstract sınıf.   Receiver bu absrast sınıfı implemente eder. 

ConcreteCommand : Command'ın içindeki metodları uygulayan ve Receiverdeki metodların kullanıldığı sınıftır.  

Invoker: Command nesnesi referansına sahip ve Command içindeki metodu çalıştıran sınıftır.

Receiver: Clientin iletişime geçtiği, işlemlerin olduğu gerçek nesne.

Dikkat edilmesi gereken husus,ConcreteCommand  iş yapmaz işine olduğu ilede ilgilenmez işi yapacak olan sınıfı çalıştırır.

 

Motivasyon : 

Arttırma,Eksiltme birde yaptığımız işlemi geri alma fonksiyonları olan basit bir hesap makinesi yapalım. Formumuz aşağıdaki gibi olsun. Arttıra basınca mevcut değeri 2 arttıracak, Eksilte basınca mevcut değerden 1 düşecek ve Geri Al a basınca yapılan işlemleri geri alacak bir program yapalım.

Blog image

Abstract sınıfımız :

 

 

 public interface ICommand
    {
        int  Execute(int a, int b);
        int Undo();
    }

Receiver sınıfımız:

class MathOperations
    {
        public static int Toplama(int a, int b)
        { return a + b; }
        public static int Cikarma(int a , int b)
        {
            return a - b;
        }
    }

ConcreateCommand sınıflarımız:

 

class ArtirCommand : ICommand
    {
        int _orgVal;
        public int Execute(int a, int b)
        {
            _orgVal = a;
            return MathOperations.Toplama(a, b);
        }
        public int Undo()
        {
            return _orgVal;
        }
    }

    class EksiltCommand : ICommand
    {
        int _orgVal;
        public int Execute(int a, int b)
        {
            _orgVal = a;
            return MathOperations.Cikarma(a, b);
        }
        public int Undo()
        {
            return _orgVal;
        }
    }

 

Invoker sınıfımız:

 //Invoker
    class CommandManager
    {
        readonly Stack<ICommand> _commands = new Stack<ICommand>();
        public int Artir(int val, int diff)
        {
            var cmd = new ArtirCommand();
            val = cmd.Execute(val,diff);
            _commands.Push(cmd);
            return val;
        }
        public int Eksilt(int val, int diff)
        {
            var cmd = new EksiltCommand();
            val  = cmd.Execute(val, diff);
            _commands.Push(cmd);
            return val;
        }

        public int GeriAl()
        {
            if (_commands.Count > 0)
            {
                var cmd = _commands.Pop();
                return cmd.Undo();
            }
            throw new Exception("Stack Bos");
        }
    }

not:  işlemlerin nesne olarak karşılıklarını saklama işi için bu sınıfta farkettiğiniz gibi _commands adında bir Stack kullanacağız.
Formun butonlarının kodları ise:

 form1

 readonly CommandManager _commandManager = new CommandManager();
        int _val;
        public int Value
        {
            get
            {
                return _val;
            }
            set
            {
                _val = value;
                textBox1.Text = _val.ToString();
            }
        }

        private void btnArttir_Click(object sender, EventArgs e)
        {
           Value =  _commandManager.Artir(Value, 2);
        }

        private void btnEksilt_Click(object sender, EventArgs e)
        {
          Value = _commandManager.Eksilt(Value, 1);
        }

        private void btnGeriAl_Click(object sender, EventArgs e)
        {
             Value = _commandManager.GeriAl();
        }

 

Çıktı : 

Blog image

2015 : memet tayanç