C# Builder Design Pattern(Builder Tasarım Deseni)
Merhaba,
Bu yazımızda Creational Patterns(Oluşturucu Kalıplar) kategorisine giren Builder Design Pattern üzerine uzun uzun ve bol örnekler eşliğinde bir içerik oluşturacağız.
Üzerine çalıştığımız projenin ihtiyaçları arttıkça, uygulamada kullanacağımız nesnelerin maliyetleride doğru orantılı olarak artmaktadır. Hal böyleyken, kăh uyguladığımız tasarım desenlerinin içerikleri geliştirilmekte kăh farklı tasarımsal yaklaşımlara ihtiyaçlar hissedilmektedir.
Builder Design Pattern adı üzerinde bir inşaatçı görev üstlenen yaklaşım sergilemektedir. Projemiz inşa süresindeyken oluşturacağımız bazı nesnelerin üretimleri oldukça maliyetli olabilir, zamanla bu nesnelerin yapısı değişebilir yahut güncellenebilir. Anlayacağınız nesne üzerinde her türlü dinamik süreç yaşanabilir. İşte bu tarz inşa durumlarında Builder D.P. ile ilgili nesneler genişletilebilir bir hale getirilmekte ve en önemlisi kod karmaşalığı minimize edilmektedir.
Aslında yazımızın devamında bol bol örneklendirerek somutlaştıracağımız adıma gelmeden evvel bu deseni neden tercih ettiğimiz üzerinde teorik olarak biraz daha durmak istiyorum.
Yukarıda bahsettiğimiz o kompleks ve maliyetli nesnelerin oluşturulmasından Builder dediğimiz sınıf sorumlu tutulacaktır. Client, hangi nesnenin oluşturulacağını türü aracılığıyla belirteceğinden dolayı oluşturulacak asıl nesneyle ve oluşturulmasıyla ilgilenmeyecektir. Dolayısıyla istemciyi maliyetli nesnelerin üretimsel sorumluluğu olan bu tarz bir işlevden kurtarmış olacağız. Ayriyetten, oluşturulacak nesnenin gerekli tüm donanımını ilgili desen halledeceği için istemciye sadece ne istediğini bilmek kalacaktır.
Şimdi yavaş yavaş Builder D.P.’e pratikten girmeye başlayalım.
Evet, yukarıdaki şemayı incelerseniz eğer gördüğünüz gibi “Builder”, “ConcreteBuilder”, “Product” ve “Director” isminde dört adet terimimiz mevcuttur. Builder paterninin senaryosunda kullanacağımız bu aktörler kimlermiş inceleyelim;
- Product
İnşa sonucunda elde etmek istediğimiz nesnemizdir. Şemayı incelerseniz eğer ConcreteBuilder sınıfı tarafından üretilmektedir. - ConcreteBuilder
Gördüğünüz gibi Product ismini verdiğimiz nesneyi oluşturmaktadır. Oluşturulacak Product’ın temel özellik ve donanımını sağlayan sınıftır. - Builder
Product nesnesinin oluşturulması için gerekli arayüzü sağlar. Dikkat ederseniz ConcreteBuilder nesnesi ile kalıtsal bir durum söz konusudur. - Director
Yaptığımız tasarım sonucunda bir Builder referansı üzerinden Client tarafından nesne üretim talebine cevap verir.
Konuyla ilgili terminoloji aşamasından sonra sıra bir kaç senaryo üzerinden Builder desenini örneklendirmeye geldi.
1. Senaryo

Öncelikle ele alacağımız senaryomuz birbirinden farklı özellik ve markalara sahip araba üretecek bir yapı oluşturmaktadır.
Burada oluşturmak istediğimiz ürün araba olacağı için Product nesnemiz Araba sınıfı olacaktır. Arabayı ürettirecek olan Opel, Ford, Mercedes vs. gibi birbirlerinden farklı özellik teşkil edecek olan tüm sınıflarımız ConcreteBuilder sınıfımız olacaktır.
Öncelikle Product Class’ımızı oluşturuyoruz.
//Product Class
class Araba
{
public string Marka { get; set; }
public string Model { get; set; }
public double KM { get; set; }
public bool Vites { get; set; }
public override string ToString()
{
return $"{Marka} marka araba {Model} modelinde {KM} kilometrede {Vites} vites olarak üretilmiştir.";
}
}
Şimdi ise Builder arayüzümüzü oluşturalım.
//Builder
abstract class IArabaBuilder
{
protected Araba araba;
public Araba Araba
{
get { return araba; }
}
public abstract void SetMarka();
public abstract void SetModel();
public abstract void SetKM();
public abstract void SetVites();
}
Burada dikkat etmeniz gereken husus, Builder sınıfımızda Araba referansını protected olarak işaretlememizdir. Bunun sebebi, bu Builder Class’ın uygulanacağı Derived Class’lardan bu fielda erişilebilmesi içindir.
Ardından ConcreteBuilder nesnelerimizi oluşturalım.
//ConcreteBuilder Class
class OpelConcreteBuilder : IArabaBuilder
{
public OpelConcreteBuilder()
{
araba = new Araba();
}
public override void SetKM() => araba.KM = 100;
public override void SetMarka() => araba.Marka = "Opel";
public override void SetModel() => araba.Model = "Astra";
public override void SetVites() => araba.Vites = true;
}
//ConcreteBuilder Class
class ToyotaConcreteBuilder : IArabaBuilder
{
public ToyotaConcreteBuilder()
{
araba = new Araba();
}
public override void SetKM() => araba.KM = 150;
public override void SetMarka() => araba.Marka = "Toyota";
public override void SetModel() => araba.Model = "Corolla";
public override void SetVites() => araba.Vites = true;
}
//ConcreteBuilder Class
class BMWConcreteBuilder : IArabaBuilder
{
public BMWConcreteBuilder()
{
araba = new Araba();
}
public override void SetKM() => araba.KM = 25;
public override void SetMarka() => araba.Marka = "BMW";
public override void SetModel() => araba.Model = "X Bilmem Kaç";
public override void SetVites() => araba.Vites = true;
}
Son olarak Director Class’ı oluşturalım.
//Director Class
class ArabaUret
{
public void Uret(IArabaBuilder Araba)
{
Araba.SetKM();
Araba.SetMarka();
Araba.SetModel();
Araba.SetVites();
}
}
Evet, gördüğünüz gibi Araba üretimi için Builder D.P.’i uygulamış olduk. Şimdi bir Client tarafından araba talebinde bulunabiliriz.
//Client
class Program
{
static void Main(string[] args)
{
IArabaBuilder araba = new OpelConcreteBuilder();
ArabaUret uret = new ArabaUret();
uret.Uret(araba);
Console.WriteLine(araba.Araba.ToString());
araba = new ToyotaConcreteBuilder();
uret.Uret(araba);
Console.WriteLine(araba.Araba.ToString());
Console.Read();
}
}
Yukarıdaki yaptığımız işlemin çıktısı aşağıdaki gibidir.

Gördüğünüz gibi, her ne kadar ConcreteBuilder nesneleri üzerinde çalışmış olsakta aslen arka planda Product nesnesi üretilmektedir. Sadece o anki çalışılan ConcreteBuilder nesnesine özel bir Product inşa edilmektedir.
2. Senaryo

Builder desenini daha da açmak için bir başka örneğimizde bir firmanın farklı profildeki çalışanlarına göndereceği promosyonlardır. Burada farklı çalışan profilleri için özel geliştirilecek promosyon ürünlerinin geliştirilmesi safhasındaki üretim karmaşıklığı, Builder deseni ile istemciden uzaklaştırılabilir.
Bu senaryoda promosyon ürünü Product iken, ürünün gönderileceği çalışan profilleri ConcreteBuilder sınıflarına denklik göstermektedir.
Öncelikle promosyonumuz olan Product nesnemizi oluşturalım.
//Product
class Promosyon
{
public string UrunAdi { get; set; }
public int PromosyonNumarasi { get; set; }
public string CalisanProfili { get; set; }
public override string ToString()
{
Console.WriteLine($"{PromosyonNumarasi} numaralı {UrunAdi} promosyonu {CalisanProfili} calışanlarına verilmiştir.");
return "";
}
}
Ardından Product nesnesini oluşturmak için ConcreteBuilder sınıflarının arayüzü olacak Builder sınıfını tasarlayalım.
//Builder Class
abstract class PromosyonBuilder
{
protected Promosyon promosyon;
public Promosyon Promosyon
{
get
{
return promosyon;
}
}
public abstract void SetUrunAdi();
public abstract void SetPromosyonNumarasi();
public abstract void SetCalisanProfili();
}
Şimdi sıra Product nesnemizi oluşturacak ConcreteBuilder sınıflarını oluşturmaya geldi.
//ConcreteBuilder Class
class AmirlerConcreteBuilder : PromosyonBuilder
{
public AmirlerConcreteBuilder()
{
promosyon = new Promosyon();
}
public override void SetCalisanProfili() => promosyon.CalisanProfili = "Amirler";
public override void SetPromosyonNumarasi() => promosyon.PromosyonNumarasi = 100;
public override void SetUrunAdi() => promosyon.UrunAdi = "Job";
}
//ConcreteBuilder Class
class IscilerConcreteBuilder : PromosyonBuilder
{
public IscilerConcreteBuilder()
{
promosyon = new Promosyon();
}
public override void SetCalisanProfili() => promosyon.CalisanProfili = "İşçiler";
public override void SetPromosyonNumarasi() => promosyon.PromosyonNumarasi = 270;
public override void SetUrunAdi() => promosyon.UrunAdi = "Ceket";
}
//ConcreteBuilder Class
class KadinlarConcreteBuilder : PromosyonBuilder
{
public KadinlarConcreteBuilder()
{
promosyon = new Promosyon();
}
public override void SetCalisanProfili() => promosyon.CalisanProfili = "Kadınlar";
public override void SetPromosyonNumarasi() => promosyon.PromosyonNumarasi = 125;
public override void SetUrunAdi() => promosyon.UrunAdi = "Tesbih";
}
Son olarak nesne üretimini gerçekleştirecek Director sınıfımızı inşa edelim.
//Director Class
class PromosyonGonder
{
public void Gonder(PromosyonBuilder builder)
{
builder.SetCalisanProfili();
builder.SetPromosyonNumarasi();
builder.SetUrunAdi();
}
}
Son olarak yaptığımız bu deseni deneyelim.
class Program
{
static void Main(string[] args)
{
PromosyonBuilder promosyon = new AmirlerConcreteBuilder();
PromosyonGonder gonder = new PromosyonGonder();
gonder.Gonder(promosyon);
promosyon.Promosyon.ToString();
promosyon = new KadinlarConcreteBuilder();
gonder.Gonder(promosyon);
promosyon.Promosyon.ToString();
Console.Read();
}
}
3. Senaryo

3. ve son senaryomuzda yemek yapımıdır. Burada yemek Product’umuz olurken; sulu, etli, sebzeli vs. ConcreteBuilder sınıflarımız olacaktır.
Öncelikle Product’ımızı geliştirelim.
enum YemekTipi
{
Sulu, Etli, Sebzeli
}
//Product
class Yemek
{
public YemekTipi YemekTipi { get; set; }
public string YemekAdi { get; set; }
public double Tuz { get; set; }
public override string ToString()
{
Console.WriteLine($"{YemekTipi} -> Tuz Oranı : {Tuz}");
return base.ToString();
}
}
Şimdi sıra Builder sınıfımızı oluşturmada.
//Builder Class
abstract class YemekBuilder
{
protected Yemek yemek;
public Yemek Yemek
{
get
{
return yemek;
}
}
abstract public void SetYemekTipi();
abstract public void SetYemekAdi();
abstract public void SetTuz();
}
Sıra ConcreteBuilder sınıflarımızı oluşturmaya geldi.
//ConcreteBuilder Class
class SuluYemekConcreteBuilder : YemekBuilder
{
public SuluYemekConcreteBuilder()
{
yemek = new Yemek();
}
public override void SetTuz() => yemek.Tuz = 75;
public override void SetYemekAdi() => yemek.YemekAdi = "Çorba";
public override void SetYemekTipi() => yemek.YemekTipi = YemekTipi.Sulu;
}
//ConcreteBuilder Class
class EtliYemekConcreteBuilder : YemekBuilder
{
public EtliYemekConcreteBuilder()
{
yemek = new Yemek();
}
public override void SetTuz() => yemek.Tuz = 65;
public override void SetYemekAdi() => yemek.YemekAdi = "Etli Pilav";
public override void SetYemekTipi() => yemek.YemekTipi = YemekTipi.Etli;
}
//ConcreteBuilder Class
class SebzeliYemekConcreteBuilder : YemekBuilder
{
public SebzeliYemekConcreteBuilder()
{
yemek = new Yemek();
}
public override void SetTuz() => yemek.Tuz = 15;
public override void SetYemekAdi() => yemek.YemekAdi = "Pırasa";
public override void SetYemekTipi() => yemek.YemekTipi = YemekTipi.Sebzeli;
}
Şimdide Director sınıfımı oluşturalım.
//Director Class
class YemekYapici
{
public void YemekYap(YemekBuilder builder)
{
builder.SetTuz();
builder.SetYemekAdi();
builder.SetYemekTipi();
}
}
Kullanalım…
class Program
{
static void Main(string[] args)
{
YemekBuilder builder = new SuluYemekConcreteBuilder();
YemekYapici yemekYapici = new YemekYapici();
yemekYapici.YemekYap(builder);
builder.Yemek.ToString();
builder = new EtliYemekConcreteBuilder();
yemekYapici.YemekYap(builder);
builder.Yemek.ToString();
Console.Read();
}
}
Sanırım bu yazımızda da Builder D.P.’i yeterince detaylandırdığımızı düşünüyorum.
Okuduğunuz için teşekkür ederim…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar dilerim…




eline sağlık , ilk örnekte abstract class’lar “I” ile isimlendirilmezler interface’ler I ile isimlendirilir.
Teşekkürler,
Arada dalgınlık oluyor… 🙂
Merhaba,
Çok güzel bir anlatım.
Teşekkürler.
Faydalandıysanız ne mutlu 🙂
Eline sağlık kardeşim. Severek okuyorum.
Bu builder pattern değil, prototype pattern ile factory patterni karıştırıp biraz builder ismi eklemişsiniz. Tekrardan revize etmenizi öneririm.