Tasarım Kalıpları: – Builder (Oluşturucu) Kalıbı – I

Builder, GoF’un nesne yaratma kalıplarından bir diğeridir. Özellikle karmaşık olan nesnelerin oluşturulması için kullanılır. Dilimize “oluşturucu” (ya da daha yakın bir çeviriyle “binacı” ya da “bina eden”) olarak çevrilebilecek olan bu kalıbın amacıyla ilgili GoF’da şu bilgiler bulunmaktadır:

Intent: Separate the construction of a complex object from its representation so that the same construction process can create different representations.

Amaç: Karmaşık bir nesnenin oluşturulmasını, sunumundan ayırın öyle ki aynı oluşturma süreci farklı sunumlar yaratabilsin.

Bazen bir nesneyi yaratmak, basit bir yapılandırıcı çağrısından ziyade bir süreç içinde olur. Böyle durumlarda hem nesne karmaşıktır hem de nesneyi yaratma süreci karmaşıktır. Bu türden nesneleri oluşturmak için gerekli süreç Builder kalıbı olarak modellenebilir. 

Bir nesnenin karmaşık olduğunun en temel göstergesi, çok parametre alan kurucularıdır (constructor): Telescoping constructor anti-pattern. Kurucuya geçilen parametrelerin de oluşturulması gerektiği düşünüldüğünde bu sürecin soyutlanması gerektiği açık hale gelir. Factory Method ve Abstract Factory bu problemi çözmez.  Çünkü J. Bloch’un da Effective Java kitabında söylediği gibi bu iki kalıp ta kurucular gibi büyüme (scale) probemi olan yapılardır.

Aşağıdaki sınıf diyagramını ele alalım örneğin. Bu örnekteki Computer sınıfı, böyle bir örnek için uygun karmaşıklıkta bir sınıftır. Diyagramdan da görüldüğü gibi bu sınıfın 4 tane kurucusu vardır. Bu tipten karmaşık kuruculara sahip olan sınıfların nesnelerini oluşturmanın bir yolu argüman alan kuruculardan hangisini kullanacağınıza karar verip, sırayla, geçilecek argümanları oluşturup sonrasında ise kurucunun istediği sırayla oluşturulan argümanları geçmektir.

ClassDiagram1

Computer nesnesini bu şekilde oluşturan client kod örneği şöyle olabilir:

package org.javaturk.dp.pattern.gof.creational.builder.problem;

import org.javaturk.dp.pattern.gof.creational.builder.CPU;
import org.javaturk.dp.pattern.gof.creational.builder.Display;
import org.javaturk.dp.pattern.gof.creational.builder.GraphicCard;
import org.javaturk.dp.pattern.gof.creational.builder.HardDrive;
import org.javaturk.dp.pattern.gof.creational.builder.Keyboard;
import org.javaturk.dp.pattern.gof.creational.builder.Mouse;
import org.javaturk.dp.pattern.gof.creational.builder.RAM;

public class Test {

	public static void main(String[] args) {
		Test test = new Test();
		
		CPU cpu1 = test.produceCPU();
		RAM ram1 = test.produceRAM();
		HardDrive hd1 = test.produceHardDrive();
		GraphicCard graphicCard1 = test.produceGraphicCard();
		Computer computerWithoutDisplayKeyboardAndMouse = new Computer("computerWithoutDisplayKeyboardAndMouse", cpu1, ram1, hd1, graphicCard1);
		computerWithoutDisplayKeyboardAndMouse.start();
		
		
		CPU cpu2 = test.produceCPU();
		RAM ram2 = test.produceRAM();
		HardDrive hd2 = test.produceHardDrive();
		GraphicCard graphicCard2 = test.produceGraphicCard();
		Display display2 = test.produceDisplay();
		Keyboard keyboard2 = test.produceKeyboard();
		Mouse mouse2 = test.produceMouse();
		Computer computerFull1 = new Computer("computer full", cpu2, ram2, hd2, graphicCard2, display2, keyboard2, mouse2);
		computerFull1.start();
	}

	public CPU produceCPU(){
		return new CPU();
	}
	
	private RAM produceRAM() {
		return new RAM();
	}
	
	public HardDrive produceHardDrive(){
		return new HardDrive();
	}
	
	public GraphicCard produceGraphicCard(){
		return new GraphicCard();
	}
	
	private Display produceDisplay() {
		return new Display();
	}

	private Keyboard produceKeyboard() {
		return new Keyboard();
	}

	private Mouse produceMouse() {
		return new Mouse();
	}
}

Görüldüğü gibi yukarıdaki koddaki Computer nesnelerinin oluşturulması, pek çok parametre alan ve birbirine benzeyen, dolayısıyla ayırt edilmeleri pek de kolay olmayan kurucu çağrılarıyla yapılmaktadır. Bu tip kurucu çağrılarının hataya elverişli olması bu yaklaşımın bir diğer zorlu tarafını oluşturur.

Yukarıdaki yaklaşıma alternatif olarak J. Bloch, kitabında “JavaBean” yaklaşımından bahsetmektedir. JavaBean nesneleri bildiğimiz gibi, argüman almayan kurucu çağrısı ile bol miktarda “set” metodu çağrısıyla oluşturulur. Yani client kodu şöyledir:

...
		Computer computerFull2 = new Computer();
		computerFull2.setName("computer full2");
		CPU cpu3 = test.produceCPU();
		computerFull2.setCpu(cpu3);
		RAM ram3 = test.produceRAM();
		computerFull2.setRam(ram3);
		HardDrive hd3 = test.produceHardDrive();
		computerFull2.setHd(hd3);
		GraphicCard graphicCard3 = test.produceGraphicCard();
		computerFull2.setGraphicCard(graphicCard3);
		Display display3 = test.produceDisplay();
		computerFull2.setDisplay(display3);
		Keyboard keyboard3 = test.produceKeyboard();
		computerFull2.setKeyboard(keyboard3);
		Mouse mouse3 = test.produceMouse();
		computerFull2.setMouse(mouse3);
		computerFull2.start();
...

Yukarıdaki kod parçası, teleskopik kurucu problemini daha zor bir problemle değiştirmektedir: Çok sayıda set metodunun çağrılmasının zorluğu yanında, bu sırada yapılabilecek olan eksik çağrıların Computer nesnesinde sebep olacağı durum problemlerini çözmek ciddi zaman alacaktır. Bu türden nesne oluşturan kodların copy-paste yoluyla pek çok yere dağılacağı da düşünülürse, nasıl bir problemle karşı karşıya kalındığı ortaya çıkar. JavaBean yaklaşımı ayrıca nesnenin durumunu devamlı değiştirilebilir kılmaktadır.

Görüldüğü gibi karmaşık nesnelerin tekrar kullanılabilir şekilde oluşturulmasını sağlayacak bir yaklaşım, yukarıdaki problemleri ortadan kaldıracaktır. Böyle bir yaklaşım Builder kalıbıyla mümkün olabilir. Builder kalıbıyla böyle bir problemin nasıl çözülebileceğini bir sonraki yazıda ele alalım.

Toplam görüntülenme sayısı: 2617