23 Mayıs 2013 Perşembe

Tasarım Desenleri (Design Pattern)

Design Patterns


Tasarim Desenleri Nedir?


Nesne yönelimli programlamada, sinif ve nesneler arasindaki iliskinin en iyi nasil olmasi gerektigini açiklayan yönteme tasarim desenleri denir. Algoritmalar programin matematiksel olarak nasil çalismasi gerektigine karar verdigimiz yerken, tasarim desenleri yazilim tasarimi sorunlariyla ilgilenir. Tasarim desenleri, yazilim tasariminda karsimiza çikan sorunlari esnek, yeniden kullanilabilir, basarili çözümler getiren bir takim hazir kaliplardir.

1994 yilinda ise, Gang of Four (dörtlü çete) olarak bilinen "Erich Gamma", "Richard Helm", "Ralph Johnson" ve "John Vlissides" isimli yazarlar "Design Patterns: Elements of Reusable Object-Oriented Software" adli kitabi yazdilar ve bu en yaygin olarak kullanilan 23 deseni en çok kullanilan isimleriyle bu kitapta topladilar.

En Yaygin 23 Tasarim Deseni

Creator Design Patterns (Kurucu Tasarim Desenleri)

Abstract Factory (Soyut Fabrika)
Builder (Kurulum Nesnesi)
Factory Method (Fabrika Yordami)
Prototype (Kopya Nesne)
Singleton (Tek Nesne)

Structural Design Patterns (Yapisal Tasarim Desenler)

Adapter (Adaptör)
Bridge (Köprü)
Composite (Agaç Yapisi)
Decorator (Dekorasyon)
Facade (Ön Yüz)
Flyweight (Hafif Agirlik)
Proxy (Özdes Nesne)

Behavioral Design Patterns (Davranissal Tasarim Desenler)

Chain of Responsibility (Sorumluluk Zinciri)
Command (Komut)
Interpreter (Yorumlayici)
Iterator (Tekrarlayici)
Mediator (Arabulucu)
Memento (Hatirlayici)
Observer (Gözlemci)
State (Durum)
Strategy (Strateji)
Template Method (Kalip Yordam)
Visitor (Ziyaretçi)

Creational Pattens (Kurucu Tasarim Deseni):


Abstract Factory (Soyut Fabrika):


Bazi durumlarda birbiriyle iliskili biden fazla nesne yaratmak gerekir.




Örnegin yandaki sekilde ProductA1 sinifi nesnesi ile ProductB1 sinifi nesnesi bilikte kullanilacaktir. Benze sekilde ProductB2 ve ProductA2 nesneside birlikte kullanilacaktir bu durumda birbirleriyle iliskili nesneleri yaratan(Nesne Ailesi) farkli fabrika siniflari olusturulur. 



Bu fabrika siniflari ortak bir soyut fabricadan üretilir.














Builder (Kurulum Nesnesi):


Kurulumu saglayan bir çok kurucu nesne olabilir. Tek bir genel kurucu üst sinif altinda toplanirlar.

Kurucu nesnelerden birini alip kurulumu yöneten bir tane yönetici sinif olur. Client belli bir parametreye göre kurucu nesneyi seçer ve bunu yönetici sinifa geçirir. Ardindan yönetici sinif ihtiyaci olan nesneyi alir.
Bu desen kurucu bir desen oldugu için nesne olusturma ve yaratma sorumlulugunu ilgili desene verir. Ayrica varsayilan degerler kullanildigi için nesnenin yaratilmasi basitlesir ve bir düzen içinde gerçeklesir.

Ayrica üst sinifin kurulum nesnesinin altina baska kurulum nesneleride gelebilir, sistemin genislemesinide bu sekilde kolaylastirmis oluruz.

Bu deseni yaratilacak nesne karmasik, anlasilmasi zor oldugunda ve bir düzen içinde olmasi gerektigi durumlarda kullanabiliriz.






Factory (Fabrika Methodu) :

Bu desende yaratma isi ayri fabrika siniflari yerine methodlara atanir. Yaratici(fabrika) metotlar, yaratilan nesneleri kullanacak olan siniflarin üyeleri olabilirler; yani farkli neneler içinden içinden kendilerine uygun olanlari yaratip kullanabilirler.

Örnek olarak; grafik ve metin dosyalarinin oldugu farkli tipteki dosyalari kullanan farkli dokümanlar olsun. Grafik dosyasi grafik, metin dosyasi da metin dosyalari otusturup kullansin. Ileride sisteme yeni dosya tipleri ve ve yeni dokümanlarin gelebilecegini düsünelim.

Çözüm :
- Tüm dosyalari Abstract bir taban siniftan üretmeliyiz.
- Bu durumda hangi dokümanin olusturulacagina taban siniftan üretilen nesne karar verir.
- Yeni bir dosya olusturmak için alt sinif olusturuldugunda hangi dokümanin olusacagina alt siniftaki fabrika metodunda karar verilir .

Not: Fabrika metodunda yaratilan nesneleri yok etmek içinde “Dispose Method” kullanilabilir.
Dispose Method:
Desende yönetilmeyen nesneler için Dispose methodunu kullanarak onlari bellekten atabiliriz.
Bu nesneleri using ifadeiyle ya da try-catch-finally blogunun uygulandigi desenler yardimiyla bellekten atabilmek için, nesnenin sart kostugu method “IDisposable” arayüzünün yazilmasi gerekmektedir.

Prototype (Kopya Nesne):
Spesifik projelerde kullanilan ve oldukça faydali olan bir kaliptir. Data sonradan olusturulacak nesneler için bir prototype görevi üstlenen bir yapiyi olusturmak için kullanilir. Projenin bir çok noktasinda kullanilan nesnelerin bir kopyasini alip islemlere kopya nesneyle devam edilmek istendiginde bu method kullanilabilir.

Nesnelerin kopyalanmsi islemi 2 sekilde yapilabilir;
-Shallow Copy: Nesnelerin sadece referanslarinin kopyalandigi, klasik atama islemlerinde meydana gelen kopyalama biçimidir. Nesnenin sadece adresini kopyaladigi için bu yöntem prototype metoduna uygun degildir.
-Deep Copy bu yöntemde nesneler birebilir kopyalanirken yeni bir referans degiskenine atanir. C#da deger türlerinin birbirine atanmasi bu duruma bir örnek olabilir."Prototype" deseninde söz konusu olan referans türden nesnelerin "deep copy" yöntemi ile kopyalanmasidir. 

Prototype Deseninde üç temel yapi bulunur.

Soyut Prototip: Kopyalanarak yeni nesnelerin olusturulacagi siniflar için temel teskil eden ve çogunlukla içinde kopyalama islemini görecek özet bir metodun bulundugu abstract siniftir yada içinde sadece bildirimlerin yer aldigi bir arayüzdür. Ihtiyaca göre bu yapi özet(abstract) bir sinif olabilecegi gibi bir arayüzde olabilir.
Somut Prototip Nesneleri: Soyut nesneleri uygulayan ve projelerde kullanilan gerçek nesneleri temsil ederler. Kopyalama islemi soyut siniftan kalitilan bir metot ile yapilir.
Istemci(Client) Uygulama: Somut prototip nesnelerinden birer kopyasini elde edecek metot, sinif yada baska bir üye elemandir.
Not : Görüldügü üzere, "Prototype" deseni daha önce anlatmis oldugum desenlere nazaran çok kolay ve anlasilirdir. Bu kolayligin en büyük nedenlerinden biriside nesnelerin kopyalanmasi için .NET Framework’un saglamis oldugu imkanlardir.
Bütün bu bilgiler isiginda “Prototype” sinifinin UML diagrami yandaki gibidir.






Singleton (Tek Nesne):

Örnek olarak; okul sinifindan sadece tek bir nesne olusturulacak ve bu okul nesnesine projenin gerekli yerlerinden erisilecek dersek bunu nasil gerçeklestirebiliriz?
Okul nesnesnesini kim yaratacak ve bu nesneye nasil erisilecek?

Problemimiz: Bir siniftan sadece bir nesne yaratilmali, bu nesne diger nesnelere global erisim hakkina sahip olmali.

Çözüm: Sinifin içinde tekil nesne yaratip ,adresi döndüren statik bir method yazalim. Çünkü static methoslar sinifa ait henüz bir nesne olusturulmamis olsada çagirilabilirler. Bu static Method çagrildiginda henüz bir nesne olusturulmamissa olusturup adresi döndürür.


Structural Design Patterns (Yapisal Tasarim Desenler)
Adapter (Adaptör):
Bu kalip istenen isi yapan hazir siniflara sahip oldugunda ancak bu hazir sinifin arayüzünün bizim bekledigimizden farkli oldugu durumlarda kullanilir. Temel islevi bir sinifin arayüzünü baska bir sekle döüstürmektir.

Adabtör kalibi karmasik problemlerin çözümlerinde de kullanilabilir. Tasarlanan sistemin ayni isi birden fazla farkli sinifla ya da sistemle iletisime geçerek yaptigi durumlarda istenilen isi yapan hazir siniflarin arayüzleri bekledigimiz gibi olmayabilir.
Örnek olarak ;
Problemimiz: Istenilen isi yapan fakat farkli arayüzleri olan benzer birimler için kararli tek bir arayüz nasil yaratilir?
Çözüm: Birimin arayüz nesnesini adaptör nesnesi kullanarak baska bir arayüze dönüstürelim.

Bridge (Köprü):

Köprüleme metodunda abstraction ve imlamentation'i ayri tutarark ikisinide bagimsiz olarak degistirebiliriz. Burada kastedilen bir sinifin islevlerini yerine getirebilmesi için kullanilan diger siniflarin delegation'idir.

Örnek olarak : 2 farkli program yardimiyla 2 farkli sekil çizmemiz gerekiyor.

Problem: Sekillere nereden karar vericez ve hangi programla çilmeleri gerektigini nasil belirleyecegiz?

Çözüm: Birden fazla türde sekil ve birden fazla bu sekilleri çizebilecegimiz program var. Ortak kavramlarimizi belirledik. Sekil sinifi degisik sekillerin ortak özelliklerini barindiracak. Programlarin bulundugu nesne zaten nasil çizilecegini bilmektedir.

Iki grup arasinda iliski kurarken 2 olasiligimiz var ya sekiller çizim programlarini kullanacak ya da çizim programlari sekilleri kullanacak. Ilk olasilik nesne yönelimli programlamaya daha uygun oldugu için ilk seçenegimizden yola çikarak diyebiliriz ki sekiller bu durumda kullanilacak olan implementation için bir isaretçi içerir bu da aradaki köprüdür.
Composite (Agaç Yapisi):

Bazi durumlarda bir nesne belli bir is için bir nesne ile iliskilendirildigi gibi ayni is için bir nesne listesiylede iliskilendirilebilir. Esnekligi saglamak istedigimiz durumlarda nesneyle mi nesne grubuyla mi iliskili oldugumuzdan habersiz olmamiz gerekir.

Problem: Nesne gruplari veya birlesik nesneler tek bir nesne gibi çok sekilli olarak nasil tanimlamlanir?

Çözüm: Birlesik ve atomik nesneleri ayni soyut siniftan(ara yüzden) türetin, birlesik nesneler için atomik nesneleri içeren bir liste yerlestirin.
Decorator (Dekorasyon):
Kalibin temel islevi bir nesneye dinamik olarak yeni davranislar eklenmesini ve çikarilmasini saglar.

Örnegin D nesnesi d1,d2,d3 islemlerini yapiyor ancak bazen bu durum d4,d5,d1 sirasiyla da gerçeklesebiliyorsa bu durumda decorator kalibi kullanabiliriz. Decorator kaliba göre bütün islemler ayri bir decorator sinif gibi tasarlanir. Programin çalismasi sirasinda decoratör siniflardan yaratilan nesneler duruma göre siralanabilir.

Ayni arayüze sahip olmalari için ortak bir listede yer alabilmeleri için bir soyut decoratör sinifindan türetilebilir.(concrete component)



Facade (Ön Yüz):
Diyelimki eski bir proje var alinizde yakinda yerini yeni bir proje alacak böyle durumlarda eski proje için bir ön yüz(cephe) olusturulur böylece eski sistemle tek bir nesne üzerinden erisilir. Karmasik eski sistemin bütün hizmetleri kullanilmayacaksa sadece gerekli olanlar gösterilebilir. Ayrica cephe ardindaki degisiklikler asil sistemi etkilemez.
Problem : degisik ara yüzlerin kullanildigi karmasik bir altsistemimiz var. Ilerde degisebilir ve alt sistemnin bazi özellikleri hala kullanilmak durumunda kalinabilir. Alt sistemle baglanti nasil saglanir?

Çözüm:alt sisteme bir önyüz nesnesi tanimlanir.(facade) Böylece önyüz nesnesi alt sistemin bütün hizmetleri için araci olacaktir.

Flyweight (Hafif Agirlik):
Çok fazla nesnenin sistemde oldugu durumlarda, nesne sayisinin sistemde problemlere neden olmamasi için kullanilan tasarim desenidir. Hafif agirlik nesnelerini bir koleksiyonda tutmak gerekir.tutulan nesnelerin durumlari az ise ortak nesnelerin sayisi azalir ve bu koleksiyonun büyüklügünü de azaltmis oluruz.

Problem:Bir islemci sinifi düsünelim ve birde islem sinifi düsünelin islem sinifindaki her bir üye için farkli bir islem gerçeklesiyor ise islemcide bu islem yükünü hafifletmek için ne yapabiliriz?

Çözüm:her bir islem için islemci sinifi olusturmak yerine bir havuz olustugunda havuza kaydedilir ve her gelen islemde havuza bakar varsa havuzdan kullanir yoksa olusturur böylece fazladan islem olusturmamis oluruz.

Proxy (Özdes Nesne):
Client ve operasyonu gerçeklestiren methotlar arasinda bulunan bir yapi görevi görür.







Uml diyagramindaki Subject yapisi interface veya abstract sinif olarak tasarlanir ve gerçeklestirilecek operasyonlarin tanimini belirtir. RealSubject ve Proxy ise gerçek siniflardir. RealSubject sinifi operasyonu gerçeklestirecek gerçek kodlari barindirir. Proxy sinifi ise RealSubject sinifindaki operasyonlari çalistirir. Yani burada proxy nesnesi client ve operasyonu gerçeklestirecek metotlarin arasinda bulunan bir yapi görevi görür.


Behavioral Design Patterns (Davranissal Tasarim Desenler)

Chain of Responsibility (Sorumluluk Zinciri)

Bu tasarim deseni, egerki bir dizi islem islem siniflarinda tutuluyorsa ve bu islemlerin baslangiç islemleride varsa ki islem sirasi olmali ki bu islem sirasini olusturmamizi saglayan desendir.




Command (Komut)
Kullanicinin istedigi komut blogunu sarmalayarak bir nesne seklinde saklanmasini saglar.
Sarmal kod halinde saklanan bu kod blogu alici nesne için bir çözüm olusturabilir. Çöüzmlerin nesneler halinde saklanmasinin da getirisi olarak komut tasari kalibi ayni kod yapisinin tekrar tekrar kullanilabilmesine olanak saglar.

Komut tasarim kalibinin kullanildigi tüm durumlarda geçerli olan bazi ortak terimler mevcuttur. Açiklamalari ile beraber bunlar;
-Komut (Command) :Gerçeklestirilecek islem için bir ara yüz tanimlar.
-Somut Komut (Concrete Command):Alici ve gerçeklestirilecek islemler arasinda bir bag kurar, alicida karsilik düsen islemleri çagirarak çalistirma eylemini gerçeklestirir.
-Istemci (Client):Komut nesnesini olusturur ve metodun sonraki zamanlarda çagrilabilmesi için gerekli bilgiyi saglar.
-Çagirici (Invoker):Metodun ne zaman çagrilacagini belirtir.
-Alici (Receiver):Kullanici isteklerini gerçeklestirecek asil metod kodlarini içerir.




Interpreter (Yorumlayici)
Interpreter tasarim deseni, düzgün gramer ifadesindeki metinlerin sayisal veya mantiksal olarak islenmesi gereken durumlarda kullanilir. Düzgün gramer ifadesi olarak, metinde ki karakterlerin özel karsiliklari oldugu metinler olarak düsünebiliriz. Interpreter tasarim deseninde; bu özel ifadelerin islenmesi için hepsi ayni ara yüz veya abstract sinifi uygulayan siniflar tasarlanir. Interpreter tasarim deseni için Uml diyagrami asagidadir.



Interpreter tasarim deseninde 5 temel yapi bulunmaktadir.
-Context: Islenilecek metin
-AbstractExpression: Context de ki anlam teskil eden ifadeleri isleyecek siniflarin uygulamasi gereken arayüz veya abstract sinif.
-TerminalExpression: Eger özel anlam ifade eden karakter tek basina bir anlam ifade ediyor ise yani kendinden önce veya sonra gelen karakterler ile bir baglantisi olmadan yorumlaniyor ise bu siniflar TerminalExression olarak ifade edilir. AbstractExpression nesnesini uygularlar.
-NonterminalExpression: TerminalExpression lar arasinda “ve”  “esittir” gibi birbirlerine bagli olmalari veya aralarinda isleme sokulmasi gibi durumlarda NonerminalExpression tipindeki siniflar kullanilir. TerminalExpression - NonterminalExpression tipleri arasinda da iliski olabilir. NonterminalExpression siniflari içerisinde AbstractExpression tipinde referans veya referanslar olmalidir.
-Client: Context, TerminalExpression ve Nonterminal expression yapi agaçlarini olusturur ve çözümleme metodunu çalistirir.

Iterator (Tekrarlayici)

Nesne tabanli dillerde uygulama gelistirilirken en sik kullanilan yapilardan biri de koleksiyonlardir.  Iterator tasarim deseni ile koleksiyon yapisi bilinmesine ihtiyaç olmadan koleksiyon elemanlari üzerinde islem yapilabilmesini saglar. Yani iterator tasarim deseni kullanilarak koleksiyonun array, queue, list olmasi önemli olmadan, ayni sekilde elemanlarinin elde edilmesi saglanir. Koleksiyon içindeki nesnelerin nasil elde edilecegi tercihe göre belirlenebilir. Yani sonraki, ilk, son, 3. Eleman gibi istenilen sekilde elemanlara ulasilabilir. Iterator tasarim deseninde 5 temel yapi bulunur.
-Iterator: Koleksiyon elemanlari elde edilebilmesi için gerekli islemleri tanimlar.
-Aggregate: Koleksiyon barindiran nesnelerin Iterator tipinden nesne olusturacagini belirten arayüzdür.
-Concrete Aggregate: Koleksiyon barindiran nesnedir. Aggregate arayüzünü uygular ve ilgili ConcreteIterator nesnesini olusturur.
-ConcreteIterator: Aggregate yapisinda ki koleksiyon elemanlarinin elde edilmesini saglayan metotlari barindiran yani Iterator arayüzünü uygulayan gerçek iterator nesnesidir.
-Client: Bu yapiyi kullanarak koleksiyon içindeki elemanlara erisen yapidir.
-Iterator tasarim deseni için uml diyagrami asagidadir.

Mediator (Arabulucu)

Proje büyüdükçe bir çok sinif ve hirerarsi içerir. Mediator bu durumda yardimiza yetisiyor objelerin birbirlerini bilmelerine gerek kalmadan islerini yapabilmelerini saglar.


Memento (Hatirlayici)
Memento tasarim deseni nesnenin bir halinin kopyasini alip sonra bu kopyanin tekrar elde edilmesini saglar. Genelde geri al islemi için kullanilir. Memento tasarim deseni 3 yapidan olusur.
-Originator: Tamaminin veya bazi özelliklerinin kopyasinin tutulacagi nesnedir. Memento nesnesini olusturan ve geri yüklenmesinden sorumludur.
-Memento: Originator nesnesinin saklanacak özelliklerinin tanimli oldugu nesne.
-Caretaker: Saklanacak olan memento nesnesinin referansini içinde barindiran nesnedir.





Observer (Gözlemci)
Bir tasarim nesnesinde degisiklik oldugu durumlardan haberdan olmamiz gerektiginde bu tasarim deseni kullanilir. Kisaca bir network hattinda dinleyici olan bir çok nesne bir nesnenin durumunu sürekli gözlemler. Degisiklik oldugunda gözlemcilere haber verilir.

State (Durum)

Nesnenin durumu degistiginde, davranisida degisiyorsa kisaca nesneler farkli durumlarda farkli davranislar sergiliyorlarsa durum tasarim deseni kullanilabilir. Durum tasarim deseninin özelligi if/else, veya swich ifadeleriyle kontrol edilebilmesini saglar.

Strategy (Strateji)

Uml semasindan da anlasilacagi üzere Context nesnesi içinde Strategy referansi barindirir yani Strategy arayüzünden türeyen nesneleri barindirir. Strategy arayüzünde ise farkli sekillerde gerçeklestirilecek islemler tanimlanir. Sifrele – SifreCoz, Encrypte – Decrypte, LogYaz gibi. Bu islemler gerçeklestirilirken direkt ConcreteStrategyA, ConcreteStrategyB nesneleri ile degil Context nesnesi kullanilir. Context nesnesine hangi ConcreteStrategy nesnesine göre çalisacagi verilir ve kullanilacak islemler Context üzerinden çalistirilarak verdigimiz ConcreteStrategy nesnesine göre islemi gerçeklestirmis oluruz.


Template Method (Kalip Yordam)


Üst siniflarda yer alan kalip yordam, tüm alt siniflarin ihtiyaç duydugu adimlari barindirir, her yerden ulasilabildigi gibi, kaliplar olusturulurken detaylar alt siniflara birakilir.

Örnek olarak veri tabani kayit eklemek ve bir kaydin var olup olmadigini veren islemleri ele alalim. Normalde connection bir kez açilir fakat biz her islemde connecitonu açip islem bitince de kapatacagimizi düsünelim.
Bu senaryoya template tasarim deseni ile söyle bir çözüm getirebiliriz. Islemlerin tanimli olacagi bir abstract sinif tanimlariz. Bu sinifta connection açan OpenCon ve connectionu kapatan CloseCon metotlarini tanimlayalim. Her tablo için kayit ekleme ve kontrol etme islemleri farkli olacagindan Insert ve CheckData metotlarini abstract olarak tanimlayalim ki Concrete siniflarda bu metotlari overwrite edebilelim. Son olarak da Insert ve CheckData islemlerini her gerçeklestirirken her seferinde connectionu açma ve kapama islemlerini tekrar yazmamak için TemplateInsert ve TemplateCheckData isimli iki metot daha ekleyip içlerinde gerekli olan islemleri çalistiralim.

Visitor (Ziyaretçi)

Çok sayida farkli tipte nesneyle islem yapilmasi gerektigi durumlarda kullanilir.
Islem yapilacak nesnelerde her hangi bir degisiklik yapilmaz ziyaretçi nesneler olusturulur. Eger sisteme yeni nesneler eklenmiyor ama yeni islemler ekleniyorsa bu yöntem kullanilir.