Merhabalar.

Bu yazıda Composite Design Pattern‘in ne olduğundan ve kullanım şeklinden bahsedeceğiz. Öncelikle bu Design Pattern’in kullanımını ortaya çıkaran sorundan başlayalım.

Şimdi elimizde büyükçe bir kutu olduğunu düşünelim. Kutuyu açtık ve içerisinden bir cep telefonu ile yanında bir kutu daha çıktı. Telefonu bir kenara koyduk, diğer kutuyu açtık içerisinden bir dönüştürücü iki kutu daha çıktı. Bu kutuları açtığımızda da birisinden kulaklık, birisinden de şarj aleti çıktığını gördük. Bu örneği sürekli kutu içinden başka kutular çıkacak şekilde uzatabiliriz.

İşte Composite Design Pattern bu noktaya devreye giriyor. Eğer bizim uygulamamızda bu şekilde Ağaç (Tree) yapısına uyacak bir veri modellemesi ihtiyacı oluşursa Composite Design Pattern’ı kullanabiliriz. Bileşim, karma manalarına gelen Composite kelimesi de bu pattern’de component’lerin bir araya gelmesi ile oluşan bir yapı bizim için.

Gerçek dünyadan da verilen şöyle bir örnek var genel olarak. Bir alay düşünelim, bu alayın komutanı var, o komutanın altında komutandan düşük rütbeli olan ve farklı birimleri yöneten bir takım subaylar var. Bu subayların altında da yine kendi birimleri içerisindeki bir takım birimleri yöneten subaylar var. Bu şekilde dallaranak en sonun manga, takım, ekip gibi en küçük birime kadar geliniyor. Yukarıdan aşağı iletilen emirler var. En yukarıdan bir emir altındakilere veriliyor, o altındakiler kendi altındakilere emirleri iletiyor. Bu şekilde mangaya kadar varıyor.

Şimdi bir örnek uygulama yapalım ve konuyu biraz daha somutlaştıralım. Bizim örneğimiz kutu ve kutunun içindeki ürünler üzerinden olacak.

Entity.java

package com.ilkaygunel.composite;

public abstract class Entity {
    protected static StringBuffer indent = new StringBuffer();
    protected static int level = 1;

    public abstract void traverse(int[] levels);

    protected boolean printThisLevel(int[] levels) {
        for (int value : levels) {
            if (level == value) {
                return true;
            }
        }
        return false;
    }
}

Abstract sınıf tipindeki Entity sınıfını ağaç yapısı içerisinde ortak özellikler olarak düşünebiliriz. Örneğin kutu ve ürünlerin level bilgisi var, kaçını alt dizilim içerisinde olduğu bilgisi.

printThisLevel metodunu da aynı seviyedeki ürün ve kutu tespitleri için kullanılacak.

Prodcut.java

package com.ilkaygunel.composite;

public class Product extends Entity {
    private int value;
    public Product(int value) {
        this.value = value;
    }

    @Override
    public void traverse(int[] levels) {
        if (printThisLevel(levels)) {
            System.out.println(indent.toString() + value);
        }
    }
}

Product sınıfı kutuların içindeki ürünleri niteleyecek sınıftır. Parametreli bir yapılandırıcısı ve Entity’den gelip Override edilen traverse metoduna sahip. Product’lar kutular gibi alt/üst öğe tutamayacakları için onlarda sadece aynı seviye tespiti yapıp mesaj yazdırıyoruz.

Box.java

package com.ilkaygunel.composite;

import java.util.ArrayList;
import java.util.List;

public class Box extends Entity {
    private List children = new ArrayList();
    private int value;
    public Box(int val) {
        value = val;
    }

    public void add(Entity c) {
        children.add(c);
    }

    @Override
    public void traverse(int[] levels) {
        if (printThisLevel(levels)) {
            System.out.println(indent.toString() + value);
            indent.append( "   " );
        }
        level++;
        for (Object child : children) {
            ((Entity)child).traverse(levels);
        }
        level--;
        if (printThisLevel(levels)) {
            indent.setLength(indent.length() - 3);
        }
    }
}

Kutuları temsil edecek olan Box sınıfımız parametreli bir yapılandırıcıya, children listesine eklemeler yapacak add metoduna ve kendi içindekiler ile alt kutulardaki içerikleri ekrana yazdıracak traverse metoduna sahibiz. traverse metodu recursive bir çağırım yapısına sahip. Aynı level’daki

CompositeDemo.java

package com.ilkaygunel.composite;

public class CompositeDemo {
	public static void main(String[] args) {
		Box root = initialize();
		String[] arguments = { "1", "2", "3", "4", "5", "6" };
		int[] levels = new int[arguments.length];
		for (int i = 0; i < arguments.length; i++) {
			levels[i] = Integer.parseInt(arguments[i]);
		}
		root.traverse(levels);
	}

	private static Box initialize() {
		Box[] nodes = new Box[7];
		nodes[1] = new Box(1);
		int[] waves = { 1, 4, 7 };
		for (int i = 0; i < 3; i++) {
			nodes[2] = new Box(21 + i);
			nodes[1].add(nodes[2]);
			int level = 3;
			for (int j = 0; j < 4; j++) {
				nodes[level - 1].add(new Product(level * 10 + waves[i]));
				nodes[level] = new Box(level * 10 + waves[i] + 1);
				nodes[level - 1].add(nodes[level]);
				nodes[level - 1].add(new Product(level * 10 + waves[i] + 2));
				level++;
			}
		}
		return nodes[1];
	}
}

MainClass sınıfımız da main ve initialize metotlarına sahip. initialize metodu yukarıdan aşağıya doğru ağaç yapısını doldurarak veri setini oluşturuyor.

initialize metodu içerisinde 7 gözlü Box tipinde nodes isminde bir dizi oluşturuyoruz.

waves adında nodes dizisini doldururken kullanılacak bir dizi tanımlayıp ilk değerlerini atadık.

İç içe 2 for döngüsü olan kısımda nodes dizisinin içerisi hiyararşik olarak dolduruluyor. Yani nodes dizisinin 3. gözü dolduruluyor, sonrasında 2. gözünde tutulan veriye bu 3. gözdeki bilgi ekleniyor. İçerideki for döngüsü de 3. gözden 6. göze kadar tüm gözlerde hiyerarşik doldurma yapıyor. Hep bir önceki göz kendinden sonra gelen gözdeki bilgileri saklıyor.

Box tipindeki nodes dizisinin 2. gözündeki eleman bizim için kök eleman olacak. Yani nodes[1]’i biz en dıştaki kutu olarak düşünebiliriz.

Konsol Çıktısı

6 levellık bir yapı kullandığımız için konsol çıktımızda bu yapıya uygun bir çıktı görüyoruz. İlk kutu içerisinde 1 numaralı ürün bilgisi ve diğer ürün ve kutuları tutan kutuyu görüyoruz. Sonrasın ikinci sırada yani ikinci kutu içerisinde 21, 22 ve 23 numaralı ürünler ile kalanları tutan kutuyu görüyoruz. 6. sıraya kadar bu düzen kendini tekrarlıyor.

1
   21
      31
      32
         41
         42
            51
            52
               61
               62
               63
            53
         43
      33
   22
      34
      35
         44
         45
            54
            55
               64
               65
               66
            56
         46
      36
   23
      37
      38
         47
         48
            57
            58
               67
               68
               69
            59
         49
      39

Bu yazıda anlatacağım da bu kadar arkadaşlar.

Composite Design Pattern’da dikkat etmemiz gereken 2 nokta var. Birincisi eğer hiyerarşik bir düzene, iç içe olacak şekilde taşınacak yapıya ihtiyacınız var ise bu pattern sizin için uygun olabilir. İkincisi de ortak olan özellikleri bir interface ya da abstract sınıf içerisinde toplamak gerekecektir.

Başka yazıda görüşene kadar sağlıcaklaı kalın.