Composite Design Pattern
Composite tasarım kalıbı tekil component ve birbirinden farklı componentler grubunun hiyerarşik bir yapıda benzer şekilde hareket etmesini yani kendi içlerinde birbirlerinden farklı olan bir grup nesnenin sanki tek bir bütün nesneymiş gibi kullanılmasını bileşik kalıp sağlar. Bileşik kalıpların görevi, nesneleri bir ağaç yapısında birleştirip uygulamanın genelindeki parça bütün ilişkisini yeniden düzenleyip şekillendirmektir.Peki bu tanımdan anlamamız gereken nedir? Elinizde bir class nesnesi olsun ve bu classın gerçekleştireceği bir fonksiyonalite. Aynı fonksiyonaliteyi bu class nesnelerinin oluşturduğu bir grup da gerçekleştirebilecek olsun ve hatta aynı parenttan türeyen başka class nesnelerinin oluşturduğu bir grup da olabilir ve siz de nesneler grubunun bu fonksiyonaliteyi belli bir düzen içerisinde aynı tekil nesne gibi gerçekleştirmesini istiyorsunuz. İşte bu gibi durumlarda kullanabileceğimiz en uygun tasarım deseni composite tasarım desenidir.
Yukarıda anlatılan tanımı biraz daha somutlaştıralım. Bir asker düşünün , bu asker bizim tekil bir nesnemiz ve generalden emir aldığı zaman uygulamakla yükümlü aynı zaman da bu askerlerin bir araya gelerek oluşturduğu tabur da yani nesneler topluluğu da generalden emir aldığında uygulamakla yükümlü. Buna ilaveten yine asker olan yani aynı parenttan türeyen daha üst rütbeli askerler de mesela subay, albay vs gibi rütbeler de generalden emir aldığında uygulamakla yükümlü. İşte bu gibi bir hiyerarşinin düzenlenmesinde composite tasarım deseni kullanabiliriz.
Tanımları kod ile örneklendirdiğimizde herşey kafada çok daha iyi canlanacaktır ama onun öncesinde composite tasarım deseninin üyelerini tanıyalım.
Leaf : Tekil objemizi temsil eder.
Composite : Componentlerin bir araya gelerek oluşturuğu sınıftır.
Component : Hem composite hem de component classının türeyeceği interface’dir. İstenilirse abstract class olarak da tanımlanabilir.
Şimdi composite tasarım kalıbını kullanabileceğimiz bir örnekle işi somutlaştıralım.
- Bir fotoğraf albümü oluşturduğumuzu düşünün. Bu fotoğraf albümünün özelliği her fotoğraf albümünde olduğu gibi bir fotoğrafın ismine tıklandığında o fotoğrafı göstermesi ve albüm ismine tıklandığında da o albümdeki fotoğrafların hepsini göstermesi. Buradan anlayacağınız üzere bu senaryodaki leaf’imiz tekil bir fotoğraf. Composite’imiz ise fotoğraf albümüdür. Aşağıdaki fotoğraflardan da net bir şekilde görüldüğü üzere hem tekil fotoğraf hem de fotoğraf albümündeki tüm fotoğraflar aynı anda ekranda gösterilme gibi bir fonksiyonaliteye sahiptir. Senaryoyu biraz daha kalıplaştırırsak , bu senaryoda composite tasarım kalıbı kullanmak istediğimizde kurgu aşağıdaki gibi olacaktır.
Leaf : Tekil Fotoğraf
Composite : Fotoğraf albümü
Operation : Fotoğrafın açılması ve görüntülenmesi (open-view)
- Tüm Component metotları component için anlamlı olmayabilir bu da interface segretation prensibine aykırı bir durum oluşturur.
Ne zaman Composite tasarım kalıbı kullanmalıyız ?
- Elimizde düzensiz bir nesne yapısı ve bu nesnelerin birleşimi olduğunda…
- Client tekil nesne ve nesne grupları arasındaki farklılıkları görmeksizin işlem yapmak istediğinde… (tek fotoğrafın açılması, fotoğraflar grubunun açılması)
- Nesneler grubundaki tüm nesnelerin bir düzen içerisinde aynı işi yapması gerektiğinde…
- ve kullanıcının isteği doğrultusunda aynı türden veya farklı türlerden bir nesne topluluğu kullanmak zorunda ise, karmaşadan ve karışıklıktan kurtulmak için bileşik kalıp kullanabilir.
Aşağıda kodlanmış örneğimizde ise organizasyondaki herkes bir askerdir(Soldier) ki buda bizim Component tipimiz ile ifade edilmektedir. Composite tipimiz(CompositeSoldier) isterse kendi içerisinde birden fazla başka Component(PrimitiveSoldier veya CompositeSoldier olabilir) tiplerini içerebilmelidir. Tüm askerlerin, ister Leaf ister Composite olsun uygulayacağı birde ortak operasyonumuz vardır(ExcuteOrder). İşte sınıf diagramımız ve uygulama kodlarımız.
using System; using System.Collections.Generic; namespace CompositePattern { ////// Askerlerin rütbeleri /// enum Rank { General, Colonel, LieutenantColonel, Major, Captain, Lieutenant } /// /// Component sınıfı /// abstract class Soldier { protected string _name; protected Rank _rank; public Soldier(string name,Rank rank) { _name=name; _rank=rank; } public abstract void AddSoldier(Soldier soldier); public abstract void RemoveSoldier(Soldier soldier); public abstract void ExecuteOrder(); // Hem Leaf hemde Composite tipi için uygulanacak olan fonksiyon } /// /// Leaf class /// class PrimitiveSoldier :Soldier{ public PrimitiveSoldier(string name,Rank rank):base(name,rank) { } // Bu fonksiyonun Leaf için anlamı yoktur. public override void AddSoldier(Soldier soldier) { throw new NotImplementedException(); } // Bu fonksiyonun Leaf için anlamı yoktur. public override void RemoveSoldier(Soldier soldier) { throw new NotImplementedException(); } public override void ExecuteOrder() { Console.WriteLine(String.Format("{0} {1}",_rank,_name)); } } /// /// Composite Class /// class CompositeSoldier : Soldier { // Composite tip kendi içerisinde birden fazla Component tipi içerebilir. Bu tipleri bir koleksiyon içerisinde tutabilir. private List<Soldier> _soldiers=new List<Soldier>(); public CompositeSoldier(string name,Rank rank) :base(name,rank) { } // Composite tipin altına bir Component eklemek için kullanılır public override void AddSoldier(Soldier soldier) { _soldiers.Add(soldier); } // Composite tipin altındaki koleksiyon içerisinden bir Component tipinin çıkartmak için kullanılır public override void RemoveSoldier(Soldier soldier) { _soldiers.Remove(soldier); } // Önemli nokta. Composite tip içerisindeki bu operasyon, Composite tipe bağlı tüm Component'ler için gerçekleştirilir. public override void ExecuteOrder() { Console.WriteLine(String.Format("{0} {1}",_rank,_name)); foreach(Soldier soldier in _soldiers) { soldier.ExecuteOrder(); } } } class Program { public static void Main(string[] args) { // Root oluşturulur. CompositeSoldier generalCagatay=new CompositeSoldier("cagatay",Rank.General); // root altına Leaf tipten nesne örnekleri eklenir. generalCagatay.AddSoldier(new PrimitiveSoldier("Mayk",Rank.Colonel)); generalCagatay.AddSoldier(new PrimitiveSoldier("Tobiassen",Rank.Colonel)); // Composite tipler oluşturulur. CompositeSoldier colonelNevi=new CompositeSoldier("Nevi", Rank.Colonel); CompositeSoldier lieutenantColonelZing=new CompositeSoldier("Zing", Rank.LieutenantColonel); // Composite tipe bağlı primitive tipler oluşturulur. lieutenantColonelZing.AddSoldier(new PrimitiveSoldier("Tomasson", Rank.Captain)); colonelNevi.AddSoldier(lieutenantColonelZing); colonelNevi.AddSoldier(new PrimitiveSoldier("Mayro", Rank.LieutenantColonel)); // Root' un altına Composite nesne örneği eklenir. generalCagatay.AddSoldier(colonelNevi); generalCagatay.AddSoldier(new PrimitiveSoldier("Zulu",Rank.Colonel)); // root için ExecuteOrder operasyonu uygulanır. Buna göre root altındaki tüm nesneler için bu operasyon uygulanır generalCagatay.ExecuteOrder(); Console.ReadLine(); } } }
- Görüldüğü üzere yukarıdaki AddSoldier ve RemoveSoldier metodu leaf sınıfımız olan PrimitiveSoldier için anlamsızdır. Bu sebepten composite tasarım deseninde interface segregation prensibine aykırı bir durum da söz konusudur.