Yazılım Mühendisliği

Ekim 11, 2008

Command Pattern

Filed under: Design Patterns — Yahya KOÇ @ 10:53 pm
Tags:

Nesnelerimizin üzerinde çalışacak komutları birer nesne olarak sarmalıyoruz(Encapsulation).Yani metodlarımızı birer nesne haline getiriyoruz.Böylece çalıştırdığımız metodları ve parametlerini bir yerlerde saklamış oluyoruz.Bu komut nesnelerini, bir arayüz ya da soyut bir sınıftan türütüyoruz.Böylece örneğin “Run” gibi çok genel anlamı olan bir metod içerisinde farklı farklı işlemler yapabileceğiz.

Örneğin

public class EkleCommand:ICommand
{
  public void Run()
  {
   //Veritabanına Ekle
  }
}
public class UyarCommand:ICommand
{
  public void Run()
  {
   //MailAt
  }
}
public interface ICommand
{
  void Run();  
}

Şöyle de bir kodumuz olsun

private readonly List<ICommand> commands = new List<ICommand>();
ICommand command1=new EkleCommand ();
ICommand command2=new UyarCommand();
commands.Add(command1);
commands.Add(command2);
.......
foreach(ICommand cmd in commands)
{
  cmd.Run();
}

Gördüğümüz gibi çalışacak komutlarımızı bir köşede saklıyoruz.Zamanı gelince de sırayla çalıştırabiliyoruz.Bu tür kullanım genelde içerisinde Undo-Redo metodları olan uygulamalar içindir.Örneğin Microsoft Word Geri Al(Ctrl+Z)…..

Şöyle bir uygulama tasarlayalım.Bir Müşteri web sitemize girip Elektronik kitap üzerinde okuma yapabilsin.Sayfalar arasında dolaşabilsin.Sonra yaptığı işlemleri geri alabilsin.
Önce yazdığımız kodun sequence diagramına bakalım:

Öncelikle ICommand arayüzünü inceleyelim.

    public interface ICommand
    {
        void Execute();
        void Undo();
        void Redo();
    }

Üç temel metodumuz var.Bunları uygulayabilecek farklı farklı command nesneleri oluşturabiliriz.
PdfPager adındaki sınıfımız sayfalar arasında geçiş yapmamıza yardım etsin.

public class PdfPager
    {
        public  int CurrentPageIndex { get; set; }
        public  void Read(char IslemTipi, int Miktar)
        {
            switch (IslemTipi)
            {
                case '+':
                     IleriDogruCevir(Miktar);
                    break;
                case '-':
                     GeriDogruCevir(Miktar);
                     break;
            }
        }
        private void GeriDogruCevir(int miktar)
        {
            CurrentPageIndex+=( - 1 * miktar);
        }
        private void IleriDogruCevir(int miktar)
        {
            CurrentPageIndex+=miktar;
        }
    }

PdfPager sınıfındaki Read metodu yardımıyla sayfalarımızı ileri yada geri çevirebiliyoruz.
Şimdi asıl işimizi görecek ReadCommand sınıfını inceleyelim:
    public class ReadCommand:ICommand
    {
        public PdfPager pdfPager { get; set; }
        public char IslemTipi { get; set; }
        public int Miktar { get; set; }

        public void Execute()
        {
            pdfPager.Read(IslemTipi, Miktar);
        }
        public void Undo()
        {
            ChangeIslemTipi();
            Execute();
        }
        public void Redo()
        {
            ChangeIslemTipi();
            Execute();
        }
        public void ChangeIslemTipi()
        {
            IslemTipi = IslemTipi == '+' ? '-' : '+';
        }
    }

Bu komut sınıfımızda Execute,Undo,Redo metodlarının içini dolduruyoruz.

Şimdi de Book ve Musteri sınıflarımıza bakalım.

 public class Musteri
    {
        public static void Oku()
        {
            Book book = new Book();
            book.pager.CurrentPageIndex = 28;
            book.Oku('+', 2);
            book.Oku('-', 1);
            book.Oku('+', 11);
            book.IslemleriGeriAl(3);
            book.IslemleriIleriAl(2);
            int sayfaNumarasi = book.pager.CurrentPageIndex;
        }
    }
 public class Book
    {
        private readonly List<ICommand> commands = new List<ICommand>();
        public readonly PdfPager pager=new PdfPager();
        private int CurrentCommandIndex { get; set; }
        public Book()
        {
            CurrentCommandIndex = -1;
        }
        public void Oku(char IslemTipi, int Miktar)
        {
            ReadCommand readCommand = new ReadCommand { IslemTipi = IslemTipi, Miktar = Miktar, pdfPager = pager};
            commands.Add(readCommand);
            CurrentCommandIndex++;
            readCommand.Execute();
        }
        public void IslemleriGeriAl(int times)
        {
            for (int i = 0; i < times; i++)
            {
                commands[CurrentCommandIndex].Undo();
                CurrentCommandIndex--;
            }
        }
        public void IslemleriIleriAl(int times)
        {
            for (int i = 0; i < times; i++)
            {
                CurrentCommandIndex++;
                commands[CurrentCommandIndex].Redo();
            }
        }
    }

Kitap sınıfımızda ICommand tipinde commands adında bir listemiz var.Çalışacak komutlarımızı burada tutuyoruz.(Stack de kullanılabilir).CurrentPageIndex müşterinin o anda okuduğu sayfa numarasını,currentCommandIndex o anda çalışan komut sırasını göstersin.
IslemleriGeriAl metodunda ICommand tipindeki komutlarımızın Undo metodunu çalıştırıyoruz. Böylece yapılan işlemi geri alabiliyoruz.Üç sayfa ileri gitmişsek üç sayfa geri gidiyoruz.Beş sayfa geri gitmişsek beş sayfa ileri gidiyoruz.
Book book = new Book();
book.pager.CurrentPageIndex = 28;
book.Oku('+', 2);

Bu kod bloğu sonunda CurrentPageIndex 30′u gösterecektir.(İki sayfa ilerledik)
Book book = new Book();
book.pager.CurrentPageIndex = 28;
book.Oku('+', 2);
book.Oku('-', 1);
book.Oku('+', 11);

Bu kod bloğu sonunda CurrentPageIndex 40′ı gösterecektir.(28+2-1+11)
book.IslemleriGeriAl(3);

IslemleriGeriAl metodunda üç defa Undo işlemi çalışacaktır.Bu işlemler sırasında daha önce çalıştırdığımız son üç metodun işlem tipi değiştirilerek sonuca gidilecektir.
40-11+1-2=28 Tekrar başladığımız yere döndük.
book.IslemleriIleriiAl(2);

IslemleriGeriAl metodunda üç defa Redo işlemi çalışacaktır.Daha önce çalıştırdığımız metodlardan kaldığımız sırada işlem tipi değiştirilerek sonuca gidilecektir.
En son 28.sayfada kalmıştık.
28+2-1=29
İşlemler sonucunda 29.sayfaya konumlandığımızı gördük.

Yorum yapın »

Henüz yorum yapılmamış.

Bu yazıya yapılan yorumlar için RSS beslemeleri. URI'nin geri izlemesini yap.

Yorum yapın

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Değiştir )

Twitter picture

You are commenting using your Twitter account. Log Out / Değiştir )

Facebook photo

You are commenting using your Facebook account. Log Out / Değiştir )

Connecting to %s

Theme: Rubric. WordPress.com'dan blog alın.

Follow

Get every new post delivered to your Inbox.