13 Ocak 2011 Perşembe

İzleyeci Deseni(Observer Pattern)

İzleyici Desenin yapısında rol alan iki tür sınıf vardır.İlki konu(subject) diye nitelendirilen, ikincisi ise belirli bir konuya kayıt olabilen veya çıkış yapabiliren izleyici(observer) sınıfıdır.Bu iki sınıfın çalışma prensibi söyledir.Bir konuya birden fazla izleyici kayıt olur,kayıtlı sınıflar konunun durumuna göre kendi fonksiyonlarını kullanarak bilgilendirilirler.Uml diagramı aşağıdaki gibidir.

Uml diagramını açıklayacak olursak.Konu(subject) arayüzünün izleyici(observer) ekleyip, çıkaran ve de bilgilendiren 3 fonksiyonu var.İzleyici(observer) arayüzünün ise konunun durum(state) bilgisi hakkında bilgilendirmek için kullanılan güncelle fonksiyonu var.
Şimdi bu desenin nerelerde kullanılabiliceğini ne için tasarlanmış olduğunu ve de fonksiyonlarının içinin nasıl yazılması gerektiğini anlamak için örnekler verelim.


Örnek.1:
Yaşamla alakalı bir örnek olarak.
Tasarım desenleri dersi veren bir öğretim kuruluşu var.
  • Bu dersi almak isteyenler kayıt olmak zorunda(izleyiciEkle() fonksiyonu).
  • Dersi alan kişilerin bir listesi olması gerekir ki dersleri anlatacağı yani konudan haberdar olacak izleyicileri bilsin.
  • Derse başladınız,Artık belirli bir konu hakkında size bilgi geliyor.Yani ders devam ettikçe konu güncelleniyor(izleyiciBilgilendir() fonksiyonu).
  • Daha sonra bu ders bitiyor ve çıkış yapıyorsunuz(izleyiciÇıkar() fonksiyonu).Artık konunun durumu hakkında bilgi edinemiyorsunuz.
  • Tek bir tasarım desenleri dersi var fakat birden fazla öğrenci olabiliyor.
Burada tasarım desenleri dersi konu(subject) sınıfı,öğrencileri ise izleyici(observer) sınıfı oluyor.Ders hakkında bilgilendiriliyorsunuz fakat bunun ile öğrenci ne yapıyor dersi anlatanı ilgilendirmiyor, izleyicinin ne yaptığı konuyu ilgilendirmiyor.Çünkü siz dersi anlatan için öğrenci,konu için sadece bir izleyici(observer)siniz.Yani arada sıkı bir bağ yok.(loosely coupled)

22 Nisan 2010 Perşembe

Strategy Pattern

Strateji Deseninin amacı nedir?
Genel olarak yazacak olursam yazılımın değişikliklere uyum sağlayabilir bir şekilde tasarlanmasını sağlamak.(Aslında daha fazlası var)
Örnek ile başlayacak olursak:
Diyelim ki, öğretmenim veya çalıştığım şirket benden bir kuş similasyonu yapmamı istedi.Similasyonda iki tane kuş türü var,papağan ve güvercin.İlk olarak bu deseni bilmiyorsam ne yaparım.Muhtemelen aşağıdaki şekildeki gibi bir tasarım aklıma gelir.

Şekil.1
Dedim ki benim sistemimde zaten iki tane kuş türüm var papağan ve güvercin onların ortak özelliklerini kuş isimli sınıfta toplarım sonra göster methodunu da soyut tanımlarım ve de alt sınıflarda bunların tanımlanmasını sağlarım.Ne kadar iyi bir tasarım yaptım ya mükemmel bir programcıyım ben!!!Öylemiyim?
İleriki gelişim sürecinde patronum sisteme yeni tür bir kuş ördek eklememi istedi.Kolay hemen kuş sınıfından ördek sınıfı türetirim.Ama bir sorun var ördekler yüzebilir fakat kuş ana sınıfında öyle bir method yok ne yapsam ki?
Şekil.2
Çözüm 1.Yukarıdaki gibi türettiğim ördek sınıfına yüz methodunu eklerim olur.
Çözüm 2. Kuş sınıfının içine yüz methodu eklerim yeni bir yüzebilen bir sınıf eklendiğinde örneğin flamingo hiç tekrardan yüz methodu yazmam gerekmez çünkü temel sınıfta var,Eğer yüzemeyen sınıf varsa da yüz methodunu ezerek (overriding)tanımlarım boş bırakırım yada yüzemem yazdırırım.
İki çözümde güzel istediklerimi veriyor fakat
Çözüm 1'de bir sorun var her yeni yüzebilen kuş ekleğimde yüz fonksiyonunu yazmak yerine biraz daha iyi birşey yapmam gerekir.
Çözüm 2'de de başka bir sorun var ana sınıfa yüz methodunu eklersem bütün yüzemeyen sınıfların yüz methodunu ezmek(override) yazmak tasarımımda sorun olduğunu gösteriyor.


Sonra düşündüm ki similasyonuma hiç umulmadık kuş türleri katılabilir.Kartal,Tavuk,Penguen,Devekuşu vs .Mesela papağan ötmüyor,konuşuyor.Mesela öyle bir kuş düşünün ki uçamıyor ama uçak içinde olduğu için uçuyor göstermem lazım yada roketle uçan bir similasyon kuşu istenirse ne yapacağım...

Sonra aklıma bir fikir geliyor alttaki resim gibi bir tasarım yapıyorum.

Tasarımımda ilk olarak arayüz(interface) kullandım.3 tane arayüz(interface) 1 tane soyut ana sınıfım ve 4 tane somut alt sınıfım var.Böyle bir tasarım yaparak bir anlamda istediğim alt sınıflara istediğimiz fonksiyonları verebiliyorum.Diğer bir deyişle ayrılan özellikleri arayüzlere(interface) attım istediğim özelliği arayüzlerden çekebilirim.Fakat hala bir sorunum var interface özelliği gereği uç ve öt methodlarının tanımları tekrar alt sınıflarda yapma zorundayım.Buda beni başladığım noktaya geri döndürdü kod tekrarı...
Neden sınıftan değilde arayüzden(interface) değişen öt, yüz,uç fonksiyonlarını alıyoruz?
Bunu yapmamızın nedeni Java, C# gibi dillerde sadece alt sınıfları sadece bir sınıftan türetebilmemizdir.
Java C# gibi neden tek sınıftan türetiyorlar C++ gibi izin verseler birçok sınıftan türetilsin?
Aslında Java ve C# tasarımcıları böyle bir özelliğe gerek olmadığını görmüşler ve de onun yerine arayüz(interface) yapısını kullanmışlardır.İnterfacede zaten bir sınıftır bütün mehtodları soyut olan bir sınıf.
En son olarak söyle bir tasarım yaptım

Karmaşık görünen bu tasarımda ne yaptım:
  1. İlk olarak kutu içinde görünen yapıları bir arayüzden türemiş somut sınıflardır.Bu yapı arayüzler ile somut sınıflar arası bir kapsülleme imkanını da sağladı.Somut sınıflar her kuş için değişen özellikleri barındırır.Mesela uçmadavranışı arayüzünü yaratmamızın sebebi bütün kuşların uçamamasıdır ya da aynı şekilde uçamamasıdır.beslen fonksiyonunu bir beslenmedavranışı arayüzüne almadım çünkü bütün kuşların aynı şekilde beslendiğini varsaydım.
  2. Bütün arayüzlerin referanslarını kuş sınıfında tanımladım.Daha önceki tasarımda olduğu gibi direk somut sınıflarda implement etmedim.
  3. En son olarak arayüzlere değer ataması için kuş sınıfında yüzme,ötme,uçma davranışarını ataması birde bu işlemleri yapması için methodlar ekledim.
Bunun kodlarını ekliyeceğim...

Soru 1. Neden uçmadavranışının içine uç methodunu içini uçamam yazdıracak bir somut method yaptık?
Çünkü kuş sınıfına uçmadavranışı arayüzünün referansını verdiğimiz için uçamayan bir kuş türü eklendiğinde o somut sınıfı kullanmasını istiyoruz.
Soru 2.O zaman uçamayan kuş türleri var neden kuş ana sınıfı içerisinde uçmadavranışı arayüzüne referans veriyoruz.Onu çıkartalım sonra sadece uçabilen kuş türleri içerisinde onlara referans verelim?
İşte burda bir programlama seçeneği karşımıza çıkıyor
1.Uygulamaya programlama
Ördek ö= new Ördek();
ö.Uç();
2.Arayüze programlama
Kuş k= new Ördek();
k.Uç();
Eğer soruda denilen gibi yapsaydık ördek sınıfı uçabildiği fakat uçma özelliği kuş sınıfında olmadığı için 2 deki gibi yapamayacaktık çünkü k değişkeninde uçma özelliği olmayacaktı (Hata verecekti).2. seçenekte bizim tanımladığımız k değişkenine istediğimiz özellikleri verebiliriz ördek çalışma zamanında (runtime) olsa bile uçmasını engelleyebiliriz mesela...

Strateji Deseninde uygulanan tasarım prensipleri.
  1. Değişen methodları aynı kalan methodlardan ayırın ve de onları ayrı bir sınıfta tanımlayın.
  2. Arayüze yani ana sınıfa programlama yapın.(Yukarıda kısa anlatımı var)
  3. Sınıfları türetmek yerine birleştirmeyi seçin.
Bu seçenekleri bazılarını açıkladım fakat ayrı bir sayfada bunlarada değineceğim çünkü çok temel dolayısıyla çok önemli şeyler.

Strateji Desenini bize şunu sağladı:
Gereksiz arayüz veya sınıf türettiğimiz tasarımlar kodu sürekli olarak tekrarlanmasını sağlar.Biz bu deseni uygulayarak herhangi bir algoritmanın sadece bir kere yazılarak çok rahat bir şekilde heryerde kullanılmasını sağladık.
Ayrıca yazdığımız sınıfların özelliklerinin çalışma zamanın da bile değistirebilmesini sağladık.
Bunlarda yaptığımız tasarımın değişikliklere uyum gösterebilme yetenegini kazandırdı.







Tasarım Desenleri

Neden Tasarım Desenleri Anlatıyorum?
Madde olarak sıralayacak olursak;
1."Head First Desing Patterns" kitabından güzel anlatımıyla iyi öğrendiğimi düşünmek.(Tavsiye ederim)
2. Öğrendiklerimi paylaşmak ve de bunun sayesinde daha iyi anlamaya çalışmak.(Kitabın dili ingilizce olduğu için türkçe anlatmamın yararı olmasını umuyorum.)

Tasarım Desenleri kavramı nasıl oluşmuştur?
Yazılım geliştiricileri geliştirdikleri süre içerisin bir çok tasarım problemleri ile karşılaşmış ve yeni yazılım geliştiricilerine kolaylık sağlaması amacıyla çözümlerini "Tasarım desenleri" adı altında toplamıştır.Bunu yaparak dolaylı olarak tasarım desenlerinin isimleri ile iki yazılım geliştiricisinin rahat olarak anlaşmasını sağlamıştır
Örnegin,bir programcı diğerine ben burda observer paterni kullandım diyerek rahatca kodunu başka bir programcıya anlatmış olur...
Bunun sonucunda böyle bir kavram adı altında tasarım problemleri çözümleri anlatılmaya başlanmıştır.
Anlatılacak Tasarım Desenleri
1.Strategy Pattern