Makale Özeti

Bu yazımda Decorator tasarım desenini örnek senaryo ile birlikte anlatmaya çalışacağım.

Makale

Kodlama yaparken kişisel veya kurumsal projeler olsun geliştirilen her projede nesne yönelimli programlamanın gereklilikleri ve disiplini içerisinde yazılım geliştirmeliyiz.Nesne yönelimli programa ile geliştirlen yöntemler projelerin doğrudan kalitesini ve bakımı maliyetini etkiler.Size bu konunun  önemini vurgulayacak daha bir çok şey söyleyebilirim fakat bu yazımda anlatmaya çalıştığımız decorator deseniyle ne ilgisi olduğu.Nesne yönelimli programlamanın en önemli konularında biri kalıtım(inheritance)dır.Kalıtımın basit bir tanımını yapmak istersek ; bir sınıfın public veya protected tanımlı üyeleri türetme yoluyla başka bir sınıfın devralması anlamına gelir.

Kalıtım bize ne sağlar ?

Kalıtım bize kalıtılan her sınıfın genişlediğini  ifade eder.Temel sınıfdaki özellik ve davranışlar türeyen sınıfa devredilmiş , oluşan sınıfa ek method ve özelliklerin tanımlandığını ya da olan methodların içeriğinin değiştirldiği anlamına gelir.İşte Decorator deseni de tam bu noktada devreye girer.Bu desen nesnelere dinamik olarak yeni sorumlulukların ve özelliklerin eklenmesini olan sorumlulukların çıkarılmasını sağlar.

Projelerimizde geliştirdiğimiz yapılar(component,control) genelde sıklıkla değişir ve bu değişim sürecinin optimize edilmesi gerekir.Genelde Projede var olan bir bileşenin işlevi değiştirilmek istendiğinde ya da ek işlevler eklenmek istendiğinde doğrudan asıl bileşen üzerinde gerekli modifikasyonları yaparız.Aslında bu davranış pek de doğru değildir en azından nesne yönelimli programlama prensiplerine göre.Bu prensipler içerisinde yer alan Açık kapalı Prensibi(The Open/Closed Prenciple) ne göre yazılım varlıklarının(sınıf,modül,component) genişlemeye açık değişime yani modifikasyona kapalı olması gerektiğini vurgular.Eğer gerçek bileşen üzerinde modifikasyon yapıyorsak ya da bu şeklide yapmak zorunda kalıyorsak en azından bazı prensiplere uymadığımızın farkında olmamız gerekir.

"Bir Yazılım Mühendisi yanlış yapmayan değil yaptığı yanlışın farkında olandır." Murat ÇELİK

Bir bileşene yeni bir özellik eklemek istediğimzide ya da bileşen üzerindeki validasyonların değiştirilmesini isteniyorsa olan bileşen sınıfını temel alarak(kalıtım) yeni bir bileşen sınıfı oluştururuz.Bileşene eklenilecek her bir özellik ve işlev için türetme işlemi beraberinde kalıtım zincirinin oluşmasına diğer bir değişle kalıtım deriniliğinin uzayıp gitmesi anlamına gelir ki bu yazılımın kalitesini düşürür.Sıklıkla değişen , genişlemeye ihtiyaç duyacağını düşündüğümüz sınıflar Decorator deseni dikkate alınarak tasarlanmalıdır.Decorator deseni uygulayarak yeni özellikler için kalıtım yerine kompozisyon kullanılır.Decorator deseni ile çalışma zamanında sınıfın işlevselliğini geliştirmemize yardımcı olur.

Decorator deseninde kullanılan kavramlar;

Component: Dinamik olarak işlevsellik ekleyeceğimiz asıl sınıf için bir arayüz sunar.Interface olarak tasarlanabileceği gibi abstarct sınıf olarak da tasarlanabilir.

Concrete Component: Asıl bileşen sınfıdır.

Decorator: Component arayüzünü uyguladığı gibi içerisinde Component(IComponent) tipinden bir nesne örneğide yer alır.Bu nesne örneği sınıfın yapıcı methoduna örneklenir.

Concrete Decorator: Dinamik olarak eklemeyi düşündüğümüz bütün işlevsellikler bu tipler içerisinde yar alır.

Şimdi örnek senaryo üzerinden bir kodlama yapalım.Senaryomuzda Tüketici bileşenimiz var.Bu bileşen business tarafda değişen tanımlamalar ve istekler doğrultusunda sıklıkla değişiyor.Şuan tüketiciye ait kimlik bilgileri alınırken sonraki süreçte tüketicinin iletişim bilgileri eklenecek.Yani anlayabileceğiniz gibi sıklıkla değişebilen bir sınıf.Burada Decorator desenini uygulayabiliriz.

İlk olarak yazacağımız kodun sınıf diagramını paylaşmak istiyorum.Sonrasında diagrama bakarak sınıflarımızı oluşturalım.

İk olarak IComponent arayüzünü ve onu uygulayacak asıl Asıl Componenti (Customer) yazalım.

interface IComponent
{
    bool Valid();
    void Save();
    void SetDefaultValues();
    void initilize();
}

class Customer
    :CompositeControl,IComponent
{
        
    public bool Valid()
    {
        // Validation operations
        return true;
    }

    public void Save()
    {
        // Send Component fields
    }

    public void SetDefaultValues()
    {
        // Setting Default Values
    }

    public void initilize()
    {
        //initilize method
    }
}

Şimdi de Decorator sınıfını yazalım.Bu sınıf hem IComponent arayüzünü uygulayacak hem de içerisinde bu tipe ait bir örnek barındıracak.Decorator sınıfları oluşturulurken bu örnek üzerinden yeni özelliklerin eklenmesini sağlayacaktır.Soyut sınıf olarak tasarlanır.Bu sınıfın constructor methoduna dikkat edin.

abstract class CustomerDecorator
    :IComponent
{
    internal IComponent contractComponent;

    public CustomerDecorator(IComponent contractComponent)
    {
        this.contractComponent = contractComponent;
    }

    public virtual bool Valid()
    {
        throw new NotImplementedException();
    }

    public virtual void Save()
    {
        throw new NotImplementedException();
    }

    public virtual void SetDefaultValues()
    {
        throw new NotImplementedException();
    }

    public virtual void initilize()
    {
        throw new NotImplementedException();
    }
}

Şu aşamada aslında Customer Bileşeni için değişime hazırız(modifikasyona değil).Business taraftan gelen istekler doğrultusunda bir çok değişiklik yapacağız.Hadi bu değişikliğe dair decorator sınıfı yazalım.

class CustomerNewVersion
    :CustomerDecorator
{
    public CustomerNewVersion(IComponent component)
        :base(component)
    {
    }

    public override bool Valid()
    {
        //Yeni eklenilen validasyon işlemleri

        return base.Valid();
    }

    // Yeni davranış
    public void SetHiddenField() 
    {
        //Bazı alanları gizliyoruz.
    }
}

Şimdi istemci olarak bunu kullanalım.

//Decorator sınıfının uygulanacağı gerçek bileşen sınıfı
Customer cus = new Customer();
//Decorator nesnesine component nesne örneği verilerek decorator sınıfın sahip olduğu
// ek davranışlar component nesnesi de sahip olmuş oldu.
CustomerNewVersion nCusVersion = new CustomerNewVersion(cus);
// yeni davranışlar veya olan davranışlar için eklemeler.
nCusVersion.Valid();
nCusVersion.SetHiddenField();

Bu yazıda örnek senaryo ve anlatım ile Decorator deseninin mantığını size aktarmaya çalıştım.Umarım faydalı olmuşumdur.Hepiniz iyi çalışmalar dilerim.