Merhaba nesne yönelimli programlama tekniği sevenler!

Upuzun bir aradan sonra tekrar karşınızdayım. Tasarım desenleri makalelerimize kaldığımız yerden devam ediyoruz. Sırada Composite tasarım deseni var. Hadi bakalım başlıyoruz muhabbete!

Yine her zaman olduğu gibi; bu tasarım deseninin hangi probleme çözüm olarak geliştirildiğinin üzerinde durmamız gerekiyor. Efendim problemimiz şöyle: iç içe geçebilen yani hiyerarşik bir koleksiyonu, bellekte saklamak ve üzerinde bir takım işlemler yapmak istiyoruz. Bunun için en ideal yöntem nedir?

Sevgili dostlar, bu tanımda anlaşılması gereken en önemli nokta, koleksiyonun “iç içe geçebilen – hiyerarşik” bir yapıda olduğudur. Yani koleksiyonunuz, ağaç yapısı (tree-structured) biçiminde yapılandırılmış olmalı. Nedir bu ağaç yapısı? Gelin, gözümüzde canlandırmaya çalışalım önce. Bir ağacın gövdesi, yukarı doğru çıktıkça dallara ayrılır. Bu dallar da başka dalların gövdelerini oluştururlar. Pekâlâ, bu “başka dallardan” da başka dallar türeyebilir. Fakat bir noktada “dal” bitebilir. Yani sonlu olabilir.

Gerçek hayatta neye benziyor fark ettiniz mi? Bir soy ağacı, bir şirket hiyerarşi tablosu bu tarz bir ağaç yapısı olarak tanımlanabilir.

Bu kavramı, yazılım dışı dünyadan bir örnekle anlatmaya çalışalım. Şu an, bilgisayarınızda kullanmakta olduğunuz işletim sistemine bağlı dosya sistemini düşünün. Masaüstüne bir klasör oluşturduktan sonra iç içe dilediğiniz adar klasör ekleyebilirsiniz. Aynı şekilde bu klasörlerin içinde çeşitli dosyalar da olabilir. Fakat dosyalar, bu hiyerarşide en alt sırada olacaktır. Yani, onların altına başka bir eleman ekleyemezsiniz.

Peki, nesneleri bu şekilde tutmanın ne gibi bir avantajı olabilir? Yine bu soruya klasör örneği üzerinden yanıt vermeye çalışalım. İç içe klasörlerden birini, bir başka lokasyona taşıdığınızda, bu klasöre bağlı tüm alt klasörleri ve dosyaları da birlikte taşırsınız. Bingo! Benzer bir amaç da uygulamanızda karşınıza çıkabilir.

Örneğin bir web uygulamasında, iç içe geçebilen hiyerarşik menüleri veritabanından çekerek, dinamik olarak oluşturduğunuzu varsayalım. Elbette sorgu bir kez çalıştıktan sonra, sonucunu cache’de tutmak istersiniz. İşte cache’de oluşturduğunuz bu model, veritabanındaki yapıya ne kadar benzerse size o kadar esneklik sağlayacaktır. İşte bu noktada iç içe geçebilen bir menü nesnesi inşa etmek için de composite tasarım deseninden faydalanabilirsiniz.

Bu kadar örnek yeterlidir diye düşünüyorum. Şimdi zihnimizi her şeyden arındıralım. Kolları sıvayalım ve kodların dilinde bunu nasıl yazacağımıza odaklanalım.

Senaryomuz, menü örneği gibi olsun. Bir kitap satış uygulamasında aşağıdaki gibi bir menü olduğunu varsayalım:


Bu menüye dikkatli baktığınızda nasıl sınıflandırırsınız? Bazı elemanlara alt eleman eklenebildiğini, bazılarına ise eklenemediğini söyleyebilir sanırım. Yani, Kitaplar elemanının altına yeni bir kategori ekleyebilirsiniz. Fantastik ve Bilim Kurgu elemanlarına da öyle… Ancak, Yüzüklerin Efendisi, Vakıf ve Kaplan kaplan! elemanlarının altına başka bir eleman ekleyemezsiniz.

Bu sınıflandırmadan elde ettiğimiz yapılara isim verme zamanı şimdi. Altına eleman eklenemeyecek olan sınıfa, Kitap diyelim. Eleman eklenebilenler ise Kategori olsun. Bu durumda Kitap ile Kategori sınıfları arasındaki ortak fonksiyonları belirleyebiliriz. Bu ortak fonksiyonlar da interface üzerinde tanımlanabilir.


Ben, ortak fonksiyon olarak “Goster” metodunu seçtim. Bu metot aracılığı ile hiyerarşik yapıyı ekranda göstereceğim.

İlk olarak, Kitap sınıfını oluşturalım.


Kitap sınıfının IKutuphane interface’inden gelen Goster metodunu nasıl kullandığına bir bakın. Sadece Ad özelliğini ekrana yazdırıyor.

Şimdi Kategori sınıfını yazmak üzereyiz. Ancak bu sınıf içine alt elemanlar (Kitap veya Kategori) eklenebileceğini unutmayın! O zaman kesinlikle bir Ekle metodumuz olacak. Ayrıca Goster metodu da, kendisine bağlı tüm elemanların her birinde aynı fonksiyonu çağırarak recursive bir biçimde çalışmalı.


İşte sevgili dostlar; iç içe geçebilen hiyerarşik bir koleksiyon modeli oluşturdum. Şimdi tek yapmam gereken menümü oluşturup, en üst bileşenin ortak fonksiyonunu çağırmak!


Çıktı:


Fakat sevgili dostlar, bildiğiniz gibi böyle çözümler geliştirirken ne kadar “agnostik” bir sistem tasarlarsanız o kadar kafanız rahat eder. Yani bu örnekteki gibi Kategori ve Kitap nesnelerine özel olmasındansa, daha geniş ölçekte nesneler ile çalışabilen bir bileşen yaratmak çok daha sağlıklı olacaktır. Hadi gelin, bu bileşeni de beraber yaratalım. Bu sırada da ihtiyaçlarımızı adım adım değerlendirelim.

  1. Bileşen tip-spesifik olmalı. Sadece belirli tipler bileşene eklenebilmeli.
  2. Bir bileşen, alt bileşenlerden oluşabilmeli.
  3. Eklenen her nesne, bileşen olarak kullanılabilmeli.

Bu kurallara bakarak, bileşenimizin jenerik olması gerektiğini söyleyebiliriz. Hatta bu jenerik tipi kısıtlamak (constraint) hiç de fena olmaz. O halde önce bu kısıtlayıcıyı interface’i yazalım ve uygulayalım.


Buradaki IKompositNesne interface’i sadece constraint amacıyla yazıldı. En azından kodu daha anlaşılır hale getirecek. O halde jenerik bileşenimizi yazalım. Sonra detaylara inelim.


Yukarıdaki kodda Dugum özelliği kritik öneme sahip. Eklenen T nesnesinin referansını, fiziksel olarak tutmak ile yükümlü. Böylelikle, koleksiyona eklenen her T tipi aynı zamanda bir bileşen olarak da kullanılabilecek.

Şimdi, main metodu içindeki kodları düzenleyip, Agnostik KompozitBilesen<T> nesnemizi test edelim.

Tabii ki çıktı:


Artık, yalnızca Kitap ve Kategori gibi belirli tipler ile değil, IKompositNesne interface’ini implemente eden tüm tiplerle çalışabilecek bir bileşeniniz var! Ne dersiniz? Sizce de tadından yenmez güzellikte değil mi?

Evet sevgili dostlar! Yapısal tasarım desenlerinden olan Composite tasarım desenini de böylece serimize eklemiş olduk.

Bir sonraki yazıda görüşmek üzere!


Leave a Reply