Yazılım Mühendisliği

Ekim 12, 2008

Command Pattern-2

Filed under: Design Patterns — Yahya KOÇ @ 10:43 am
Tags:

Konuyla ilgili önceki yazılar:

“Pdf Okuyucu” örneğimizi genişleterek devam ediyoruz.PdfPager adında sayfaları ileri-geri çevirmemize yardım eden bir sınıfımız vardı.Şimdi de sayfalar üzerinde not almamızı sağlayacak bir PdfWriter sınıfımız olsun.Bu sınıf yardımı ile sayfalar üzerinde not alabilelim ya da aldığımız önceki notları silebilelim.

Önce ReadCommand sınıfımızı hatırlayalım:

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 == '+' ? '-' : '+';
    }
}

Command arayüzünü hatırlayalım.

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

Yeni komut sınıfımız olan WriteCommand sınıfımızda da IslemTipi alanına(not ekle,not sil) ihtiyacımız olacak.Undo ve Redo metodları da önce işlem tipini değiştirip sonra execute metodunu çağıracak.Öyleyse ortak kod bloklarımız var.Bu durumda arayüzü soyut sınıfa çeviriyoruz.Yeni command soyut sınıfımızı inceleyelim:

public abstract class Command
    {
        public abstract void Execute();
        public  void Undo()
        {
            ChangeIslemTipi();
            Execute();
        }
        public  void Redo()
        {
            ChangeIslemTipi();
            Execute();
        }
        public void ChangeIslemTipi()
        {
            IslemTipi = IslemTipi == '+' ? '-' : '+';
        }
        public abstract char IslemTipi { get; set; }
    }

Şimdi de ReadCommad ve WriteCommand sınıfılarımızın son haline bakalım.

    public class ReadCommand:Command
    {
        public PdfPager pdfPager { get; set; }
        public override char IslemTipi { get; set; }
        public int Miktar { get; set; }
        public override void Execute()
        {
            pdfPager.Read(IslemTipi, Miktar);
        }
    }
public class WriteCommand:Command
    {
       public override void Execute()
        {
            pdfWriter.PdfNotlariniYonet(IslemTipi,Not);
        }
        public override char IslemTipi { get; set; }
        public string Not { get; set; }
        public PdfWriter pdfWriter { get; set; }
    }

PdfPager sınıfında hangi sayfada olduğumuzu gösteren CurrentPageIndex adında bir alanımız vardı.Yeni sınıfımız PdfWriter için de hangi sayfaya not aldığımızı gösterecek böyle bir alana ihtiyaç var.Aslında CurrentPageIndex alanının bu iki sınıfa da ait olmadığını direk Book sınıfının bir özelliği olduğunu görüyoruz.Öyleyse bu özelliği, Book sınıfına taşıyoruz.

Book sınıfının son durumu şöyle:

 public class Book
    {
        private readonly List<Command> commands = new List<Command>();
        public readonly PdfPager pager=new PdfPager();
        public readonly PdfWriter writer = new PdfWriter();
        private int CurrentCommandIndex { get; set; }
        public int CurrentPageIndex { get; set; }

        public Book()
        {
            CurrentCommandIndex = -1;
            writer.book = this;
            pager.book = this;
        }
        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&#91;CurrentCommandIndex&#93;.Undo();
                CurrentCommandIndex--;
            }
        }
        public void IslemleriIleriAl(int times)
        {
            for (int i = 0; i < times; i++)
            {
                CurrentCommandIndex++;
                commands&#91;CurrentCommandIndex&#93;.Redo();
            }
        }
        public void NotAl(char IslemTipi,string not)
        {
            WriteCommand writeCommand = new WriteCommand { IslemTipi = IslemTipi, Not = not, pdfWriter = writer };
            commands.Add(writeCommand);
            CurrentCommandIndex++;
            writeCommand.Execute();
        }
    }
&#91;/sourcecode&#93;

PdfWriter sınıfımız:

&#91;sourcecode language="csharp"&#93;
 public  class PdfWriter
    {
      public Book book { get; set; }
      readonly IDictionary<int,string> NotList=new Dictionary<int, string>();
      public void  PdfUzerineNotKaydet(string not)
      {
           NotList.Add(book.CurrentPageIndex,not);
      }
      public void PdfUzerineNotSil(string not)
      {
          FindAndDeleteNot(not);
      }
      private void FindAndDeleteNot(string not)
        {
           foreach(KeyValuePair<int,string> pair in NotList)
           {
               if(pair.Value==not)
               {
                   NotList.Remove(pair.Key);
                   break;
               }
           }
        }
        public void PdfNotlariniYonet(char IslemTipi, string not)
        {
            switch (IslemTipi)
            {
                case '+':
                    PdfUzerineNotKaydet(not);
                    break;
                case '-':
                    PdfUzerineNotSil(not);
                    break;
            }
        }
    }

ve nihayet Musteri sınıfı:

 public class Musteri
    {
        public static void Oku()
        {
            Book book = new Book();
            book.CurrentPageIndex = 28;

            book.NotAl('+',"deneme");
            book.Oku('+', 5);
            book.NotAl('+', "deneme2");
            book.Oku('+', 7);
            book.NotAl('+', "deneme3");
            book.Oku('+', 8);
            book.NotAl('-', "deneme");
            book.IslemleriGeriAl(2);
            int sayfaNumarasi = book.CurrentPageIndex;
        }
    }

book.IslemleriGeriAl(2); satırına gelindiğinde sayfa numarası 48,book nesnesinin içinde yer alan writer nesnesi üzerinde tuttuğumuz NotList alanı: (33,deneme2),(40,deneme3) değerlerini gösterir.
book.IslemleriGeriAl(2); satırını da çalıştırdığımzıda önce book.NotAl(‘-‘, “deneme”); komutumuz tersine çalışıp sildiğimiz “deneme” notunu geri yazacağız.Sonra da book.Oku(‘+’, 8); komutunu tersine çalıştırıp 8 sayfa geri gideceğiz.Son durumda sayfa numarası 40,book nesnesinin içinde yer alan writer nesnesi üzerinde tuttuğumuz NotList alanı: (33,deneme2),(40,deneme3),(48,deneme) değerlerini gösterir.

Temel iş mantığında hatalar,mantıksızlar olabilir.Sonuçta bu bir örnek.Önemli olan command pattern desenin işleyiş mantığını anlamak.
Tasarım desenleri ile alakalı bir kaç şey söylerek bu yazıyı tamamlamak istiyorum.Tasarım desenleri uygulanması zorunlu metodolojiler değildir.Çözüm getirdiği iddia edilen durumlar için uygunluğu daha önceden birçok kez tescil edilmiş,genelleşmiş yöntemlerdir.Her geliştiricinin kendine has durumları için kendine özel çözümleri,tasarım desenleri olabilir.Yazılımcının tasarım deseni ezberlemesi çok mantıksız ve gereksiz geliyor bana..Ben command Pattern’i ezbere bilmiyorum.Bu kodu yazdıktan sonra bile ezberimde değil.İhtiyacım olduğunda daha önceden yazılmış kodu inceleyerek kendi durumuma göre özelleştirip kullanıyorum.Bazı durumlarda “illa da tasarım deseni kullanarak yapacağım(overengineering)” diye basit bir kodu karmaşıklaştırmanın hiç bir anlamı yok.Yazılım geliştirme, deneme-yanılma yöntemine dayalı(heuristic) bir iştir.Yukarıda yazılan kod, yeni yeni deneme-yanılma yöntemleri kullanılarak daha iyi bir duruma getirilebilir.
Sağlıcakla kalın.

Yorum Yapın »

Henüz yorum yapılmamış.

RSS feed for comments on this post. TrackBack URI

Yorum bırakın

WordPress.com'da Blog Oluşturun.