Yazılım Mimarileri ve Tasarım Desenleri Üzerine

Twin Design Pattern(İkiz Tasarım Deseni)

Merhaba,

Bu içeriğimizde çoklu kalıtımı desteklemeyen C#, Java vs. gibi dillerde çoklu kalıtımı simüle etmemizi sağlayan Twin Design Pattern(İkiz Tasarım Deseni)’ı inceliyor olacağız.

Twin Pattern, yukarıdaki giriş cümlesinde de bahsedildiği üzere çoklu kalıtımı simüle edebilmek için standart bir çözüm sunan bir modeldir. Bu pattern sayesinde çoklu kalıtım ile ilgili isim çakışmaları(name clashes) sorunlarından kaçınılabilmekte ve ortaya koyduğu mantık ile çoklu kalıtımın getirdiği olası birçok sorun önlenebilmektedir.

Çoklu kalıtım(multiple inheritance), bir class’ın birden fazla base class’ı olması anlamına gelmektedir. Kimi programcılar açısından olmazsa olmaz bir özellik olarak iddia ediliyor olsa da isim çatışmalarına(name clashes), karmaşıklığa(complexity) ve tüm bunlar neticesinde verimsizliğe(inefficiency) yol açtığı için genel kanı olarak sorunlara kaynak teşkil etmekte ve bundan dolayı olumsuz bir nitelik olarak değerlendirilmektedir. Haliyle bu yüzden C# ve Java gibi dillerde çoklu kalıtım dilin doğasında engellenmiştir. Böylece zoraki olarak çoklu kalıtımdan arındırılmış kod yapısı sayesinde yazılım mimarileri doğal olarak daha temiz ve basit hale gelmiş bulunmaktadır.

Şimdi gelin, çoklu kalıtımın davranışsal yapısıyla Twin pattern’ı mukayese edebilmek için diyagram şemaları üzerinden modelleyelim.

Çoklu Kalıtımın Tipik Yapısı Twin Pattern’ın Yapısı
Yukarıdaki görseli incelerseniz eğer base class rolünde olan ‘class1’ ve ‘class2’ sınıflarından kalıtım alan ‘class3’ sınıfı mevcuttur. ‘class1’ sınıfı ‘m1’, ‘class2’ sınıfı ‘m2’ ve ‘class3’ sınıfı ‘m3’ memberlarına sahiptir ve kalıtım neticesinde ‘class3’ sınıfında tüm memberların aktarıldığı gözükmektedir. Yukarıdaki görselde ise ‘child1’ ve ‘child2’ olmak üzere iki alt sınıf görmekteyiz. Bu sınıflar kendi aralarında referanslaşmaktadırlar ve üst sınıflardaki metotları override edebilmektedirler. Böylece ‘class1’ ve ‘class2’yi miras alan bu sınıfların kendi aralarında haberleşmesi bizlere bir çoklu kalıtım senaryosu sağlamaktadır.
Şöyle ki, ‘class1’ ile ‘class2’de ki memberların kalıtımsal açıdan tekbir sınıfta toplanmasını istiyorsak bu programatik olarak(C# ve Java için) mümkün değil demiştik. Amma velakin, ilgili sınıflardan türeyen child sınıflar, o sınıfların memberlarını kalıtımsal olarak miras almaktadırlar ve ardından kendi aralarında haberleşerek bu mirasları birbirleriyle paylaşarak çoklu kalıtım(mış) gibi bir davranış sergilenmesine kapı aralamaktadırlar.

Bu durumu aşağıdaki örnek kod çalışmasıyla daha da net aydınlatabiliriz kanaatindeyim;

Öncelikle kalıtımsal olarak memberlarına erişim göstermek istediğimiz base class’ları yaratalım:

class class1
{
    public void m1()
        => Console.WriteLine($"{nameof(class1)}.{nameof(class1.m1)} fonksiyonu.");
}

class class2
{
    public void m2()
        => Console.WriteLine($"{nameof(class2)}.{nameof(class2.m2)} fonksiyonu.");
}

Ardından bu class’ların child’larını:

class child1 : class1
{
    public child2 child2 { get; set; }
    public void m3()
        => Console.WriteLine($"{nameof(child1)}.{nameof(child1.m3)} fonksiyonu.");
}
class child2 : class2
{
    public child1 child1 { get; set; }
    public void m4()
     => Console.WriteLine($"{nameof(child2)}.{nameof(child2.m4)} fonksiyonu.");
}

Yukarıdaki child sınıfların yapısına dikkat ederseniz her ikisi de birbirlerini referans etmektedirler. Şimdi bu yapıyı kullanırsak:

child1 c = new child1();
c.child2 = new child2();
c.m1();
c.child2.m2();
c.m3();
c.child2.m4();

Görüldüğü üzere ‘child1’ sınıfı üzerinden hem ‘class1’ hem de ‘class2’de ki memberlara erişim göstermiş bulunmaktayız. Yani sanki ‘child1’ her iki sınıftan da kalıtım almış gibi bir davranış sergilemektedir. Tabi bunun yanında ‘child2’nin de memberlarından(m4) istifade edebilmektedir. Bu arada yukarıdaki kodu derleyip, çalıştırdığımızda aşağıdaki gibi bir netice vereceğini unutmadan sunalım.Tabi bu neticenin birebir aynısını ‘child2’ nesnesini de aşağıdaki gibi kullanarak alabilirdik:

child2 c = new child2();
c.child1 = new();
c.child1.m1();
c.m2();
c.child1.m3();
c.m4();

Yukarıdaki yaptığımız küçük örnek üzerinden dikkat ederseniz eğer Twin nesneler birbirlerine erişebildiklerinden dolayı client bu nesnelerden sadece birine doğrudan diğerine ise o nesne üzerinden dolaylı bir yoldan erişim sağlamaktadır.

Tabi bu pattern’ın tek sınırlılığı Twin nesnelerin, birbirlerine doğrudan olan erişimleri sebebiyle sıkı bir bağımlılıklarının olmasıdır. Haa bu bağımlılık neticesinde birbirlerinin memberlarını da gönül rahatlığıyla kullanabildiklerinden dolayı bu durum göz ardı edilebilmektedir.

Twin pattern, kalıtımdan daha az verimli ve performanslı olabilir. Ama ihtiyaca binaen kullanıldığı noktalarda bu durum gönül rahatlığıyla göz ardı edilebilir.

Gerçek Senaryo

Şimdi gelin içeriğimizi gerçek bir senaryoyla taçlandırarak, noktalayalım.

SENARYO
Kesme, boyut büyültme/küçültme, birleştirme vs. gibi türlü döküman işlemleri gerçekleştiren bir uygulama geliştirdiğimizi düşünelim. Bu uygulama içerisinde ‘PDF’ ve ‘Excel’ operasyonları gerçekleştirmek için hali hazırda gelen ‘PDFOperation’ ve ‘ExcelOperation’ sınıflarının olduğunu varsayalım. Bizler ihtiyaç olarak her iki sınıftan da özellik ve fonksiyonellikleri alarak, bünyesinde toplayan ve bütünsel olarak işlem yapmamızı sağlayacak olan bir sınıfa odaklanacağız ve bu ihtiyacı Twin pattern ile sağlıyor olacağız.
ÇÖZÜM

Nihai olarak, basit ama ihtiyaç doğrultusunda oldukça etkili olan bir pattern’ı örnekler eşliğinde incelemiş olduk.
Sabırla eşlik ettiğiniz için teşekkür ederim.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Not : Kaynak kodları aşağıdaki Github adresinden edinebilirsiniz.
https://github.com/gncyyldz/TwinDesignPattern

Exit mobile version