Selam sana ey dost!

Design Patterns serisinin, Decorator yazısına hoş geldiniz! Hadi, hiç vakit kaybetmeden bu desenin nasıl bir probleme çözüm olacağını anlamaya çalışalım.

Problem

Farz edin ki var olan bir nesnenin sınıfına müdahale etmeden, ekstra bir fonksiyon ya da özellik eklemek istiyorsunuz. Şimdi hemen diyeceksiniz ki “tamam canım, bu sınıftan türeyen yeni bir sınıf yaparım ne olacak?”. Ancak bu senaryoda, söz konusu sınıfı miras alarak yeni bir sınıf türetme şansınızın olmadığını düşünün. Bunun elbette birkaç sebebi olabilir. Örneğin her ekstra fonksiyon için bir sınıf türetirseniz, bu birkaç fonksiyonu bir arada kullanmanız gerektiğinde ne yapacaksınız? İşte bir miras kaosu daha!

Ne demek mi istiyorum? Hemen örnek gelsin! Var olan nesnemizi kahve olarak düşünelim. Ekstra olarak süt, şeker ve aroma şuruplarını eklemek istiyorsunuz. Her birini ayrı ayrı kahveden türetirseniz bakınız nasıl bir yapı oluşuyor:


Bu kaosa düşmek istemiyorsak, miras yönteminden vazgeçip söz konusu nesneye dinamik olarak yeni bir fonksiyon eklemenin yolunu bulmamız gerek. İşte Decorator tasarım deseni tam olarak burada devreye giriyor.

Çözüm

Çözümü rahatça kavramak için, kahve örneğine geri dönelim. Sütlü, karamelli ve ekstra şekerli bir kahveyi hazırlamak için gerçekten de nasıl aşamalardan geçerdiniz? Bir düşünelim… Önce, kahve makinesinden kahveyi alırsınız. Bu noktada kahvenizin ekstra hiçbir özelliği yoktur. Yani elinizde sade bir kahve var. Şimdi bu kahveye süt eklemek isterseniz, yapmanız gereken tek şey; süt kâsesinden bir miktar sütü bardağınıza dökmek. Karamel mi eklemek istiyorsunuz? Buyurun, aromanın bulunduğu minik şişeden bir iki damla ekleyebilirsiniz. Ekstra şeker? Tabii ki! Şeker, hemen orada masanın üzerinde duruyor!

Gördünüz mü? Üzerine ne kadar ekstra malzeme koyarsanız koyun, nesne hala bir “kahve”. Ya da başka bir deyişle, kahvenizi dilediğiniz kadar “süsleyebilirsiniz”.

İşte Decorator deseni de tam olarak bunu yapıyor. Var olan nesnenizi süsleyerek ona yeni özellik ve işlevler kazandırıyor. Böylelikle bu özelliklerin her kombinasyonunu oluşturma şansına sahip oluyorsunuz. Hem de çok daha esnek bir biçimde!

Gerçek hayattaki örnekler çoğaltılabilir. Ama bence Decorator için en güzel gerçek hayat örneği şöyle: bir arkadaşınıza hediye alıyorsunuz. Önce hediyeyi güzelce paketliyorsunuz. Sonra paketlenmiş halde bir kutuya koyuyorsunuz. Ardından, hediye kutusunu da paketliyorsunuz. Şimdi elinizde hala aynı ürün var fakat artık bir paket formunda! İşte Decorator tasarım desenin sembolü de bu sebeple bu!

Şimdi gelelim kod örneğimize!

Geliştireceğimiz örneğin senaryosunu ele alalım önce. Uygulamamız, eposta gönderebilen bir modüle sahip olsun. Epostanın standart yapısının üzerine, içerik şifrelenmesi veya imza eklenmesi gibi ekstra operasyonlar (süsler) eklenebilsin istiyoruz. Tüm bu ekstra fonksiyonlar eklendikten sonra bile, göndereceğiniz nesne hala bir eposta nesnesi değil mi? Tıpkı kahve örneğindeki gibi yani.

O zaman, tüm mail süsleyicilerinde ortak olarak bulunan metodun Gonder() metodu olduğunu söyleyebiliriz. O halde bu ve bunun gibi metotları, bir interface altında toplayarak işe başlayabiliriz bence!

Bu arada aklıma gelmişken, biliyorsunuz ki normalde kodu yazmak yerine fotoğrafını yapıştırıyordum. Ancak bu yazıda, kodları direk yazıda göstermeye karar verdim. Ne dersiniz? Sizce nasıl devam edelim? Bu yazının altına görüşünüzü bildirirseniz çok makbule geçer vallahi.

Pekâlâ. Şimdi, çekirdek nesnemizi oluşturacak sınıfımızı yazabiliriz. Elbette bu sınıf, IMail interface’ini implemente ediyor.

Burası önemli bir nokta… GenelMail nesnesi üzerine, şu an planlama şansımızın olmadığı başka işlemler de ekleyebiliriz öyle değil mi? Başka işlemleri ekleyebilecek her sınıfa Dekoratör ismini verelim. Tüm dekoratörler çekirdek nesnemizi (genelMail) alır ve üzerine yeni bir işlev (süs) ekler. Aynı zamanda tüm dekoratörler, dekore ettikleri nesnenin ortak metotlarını içermeliler. Kutu içinde kutu kısmını hatırlıyorsunuz değil mi? İşte sınıf geliyor:

 


Hem IMail interface’ini implemente eden hem de Constructor parametresi olarak IMail interface’ini alan bir sınıf oluşturduk böylece. Bu mantıkla, bir dekoratör de başka bir dekoratör tarafından “süslenebilir” oldu.

İlk olarak GenelMail nesnesini şifrelemek istediğinizi düşünelim. Bu tasarıma göre, şifreleme gayet bir süs olabilir. O zaman bu işlem kesinlikle bir Dekoratör tarafından yapılabilir.

 

Aynı biçimde, Mail nesnesine dijital imza eklemek de ekstra bir özellik olabilir. Bu arada ben, örneği basit tutmak için Dekorator sınıflarındaki Gonder metodunda ekrana ekstra yazı yazdırdım sadece. Ama tabii ki daha karmaşık bir yapı da eklenebilir.

 


İşte yapımız hazır! Peki hem şifrelenmiş hem de imza eklenmiş bir mail nesnesi mi lazım? Alalım tabii hemen! Buyurun:

 


GenelMail nesnesini imzaliMail nesnesine, imzaliMail nesnesini de sifreliMail nesnesine ekledik gördüğünüz gibi. Bu sayede, GenelMail sınıfına hiç dokunmadan, yeni iki fonksiyon eklemiş olduk. Yani Open-Closed prensibine uyan bir tasarım inşa ettik. Peki ya çıktı?


Süper!

Peki siz de merak ediyor musunuz bu tasarım desenleri, .net ya da Java gibi kütüphaneler içerisinde nasıl kullanılıyor diye? Eğer daha önce, herhangi bir Stream nesnesi üzerinde sıkıştırma, şifreleme gibi işlemler yaptıysanız kesinlikle Decorator deseni ile geliştirilmiş sınıflardan faydalanmışsınız demektir. Hadi size bir hatırlatma yapayım:

 


Dikkat ettiniz mi? CryptoStream, GzipStream ve BufferedStream sınıfları bir Decorator sınıfından başka bir şey değil. Hepsini oluştururken bir Stream nesnesi veriyorum ve hepsinin bu nesneye erişen ortak metotları var.

Bu son örnekle bir Design Pattern’i de geride bırakmış oluyoruz sevgili dostlar.

Esen kalın!


Leave a Reply

2 Comments