Decorator Design Pattern

Decorator design pattern

Selam sana ey dost!

Design Patterns serisinin, Decorator Design Pattern 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 Design Pattern 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 Design Pattern 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 design pattern’in 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.

public interface IMail
{
   void Gonder();
}

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

 public class GenelMail : IMail
    {
        private string kimden;
        private string kime;
        public GenelMail(string kimden,string kime)
        {
            this.kimden = kimden;
            this.kime = kime;
        }
        public void Gonder()
        {
            Console.WriteLine("Mail, {0} kişisinden {1} kişine gidiyor.",kimden , kime);
        }
    }

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:

 public abstract class Dekorator : IMail
    {      
        private IMail mail;
        public Dekorator(IMail mail)
        {
            this.mail = mail;
        }
        public virtual void Gonder()
        {
           
            mail.Gonder();
        }
    }

 

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.

 public class SifreleDekorator : Dekorator
    {

        public SifreleDekorator(IMail mail) : base(mail)
        {

        }
        public override void Gonder()
        {
            base.Gonder();
            Console.WriteLine(" Şifrelendi  ");
        }
    }

 

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.

public class ImzaDekorator : Dekorator
    {
        private string imza;
        public ImzaDekorator(IMail mail, string imza) : base(mail)
        {
            this.imza = imza;
        }
        public override void Gonder()
        {
            base.Gonder();
            Console.WriteLine(imza + " olarak imzalandı");
        }
    }

 

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

 

 class Program
    {
        static void Main(string[] args)
        {
            string kimden = "Türkay Ürkmez";
            string kime = "Şişman Adam";
            IMail standartMail = new GenelMail(kimden, kime);
            IMail imzaliMail = new ImzaDekorator(standartMail, "Türkay Ürkmez");
            IMail sifreliMail = new SifreleDekorator(imzaliMail);
            sifreliMail.Gonder();

            Console.ReadLine();           

        }
    }

 

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:

            var fileStream = new FileStream("result.txt", FileMode.CreateNew);
            CryptoStream cryptoStream = new CryptoStream(fileStream, new SHA512Managed(), CryptoStreamMode.Write);
            GZipStream gZipStream = new GZipStream(cryptoStream, CompressionMode.Compress);
            BufferedStream bufferedStream = new BufferedStream(gZipStream, 64);
            using (bufferedStream)
            {
                foreach (var item in Encoding.Unicode.GetBytes("Deneme"))
                {
                    bufferedStream.WriteByte(item);
                }
            }

 

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!

11 thoughts on “Decorator Design Pattern

  1. Güzel Makale Hocam, Aslında hep hayatımızda olan ama farkında olmadığınız design patternlerdenmiş. Farkındalık yarattınız, Teşekkürler.

  2. Güzel bir anlatım olmuş elinize sağlık.
    Daha iyi ve kolay anlaşılabilirlik açısından türkçeye kazandırılan bu tip güzel yazılara saygım sonsuz.

  3. İnternette birkaç copy / paste veya ingilizce çevrilmiş (telif belirtilmeden) yazı okuduktan sonra böyle özgün bir yazıyla olayı kavramak çok güzel. Sizi özgün anlatımınızdan dolayı tebrik ederim. Sırf anlatım olsun diye konuyla alakasız örnekler yazan yazarlara burdan sitem etmiş olayım.

  4. Unity & C# ile oyun geliştiriyorum ve bu deseni kullanmak istiyorum. Fakat olmuyor. Sorum şu;
    IMail standartMail = new GenelMail(kimden, kime);
    IMail imzaliMail = new ImzaDekorator(standartMail, “Türkay Ürkmez”);
    IMail sifreliMail = new SifreleDekorator(imzaliMail);
    sifreliMail.Gonder();

    ImzaDekorator içerisindeki mail(IMail)’a standart maili attık.
    IMail imzaliMail = new ImzaDekorator(standartMail, “Türkay Ürkmez”);
    imzaliMail.Gonder(); dersek. Standart mail içerisindeki “Gonder” çalışacak. Yani konsola şunu basacak: (“Mail, {0} kişisinden {1} kişine gidiyor.”,kimden , kime);
    Sizin projede öyle olmuyor. Fakat benim projemde o şekilde oluyor.)

    Bu noktayı anlatabilir misiniz? ve Unity’de olay farklı işliyor olabilir mi?

    1. Ben IMail atamalarını “new” kullanmadan (var olan bir nesneyi) atayarak yaptığım için olmuyormuş.

      ImzaDecorator imzaDec = new ImzaDecorator(standartMail, “Türkay Ürkmez”);
      IMail imzaliMail = imzaDec.mail (Çalışmıyor)

      Peki bu çalışmayan ile;
      IMail imzaliMail = new ImzaDekorator(standartMail, “Türkay Ürkmez”);

      arasındaki var nedir ?

Leave a Reply