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.

C# Builder Design Pattern(Builder Tasarım Deseni)

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
C# Builder Design Pattern(Builder Tasarım Deseni)
Ö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.
C# Builder Design Pattern(Builder Tasarım Deseni)

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
C# Builder Design Pattern(Builder Tasarım Deseni)
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();
        }
    }

C# Builder Design Pattern(Builder Tasarım Deseni)


3. Senaryo
C# Builder Design Pattern(Builder Tasarım Deseni)
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();
        }
    }

C# Builder Design Pattern(Builder Tasarım Deseni)

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…

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. Celal dedi ki:

    eline sağlık , ilk örnekte abstract class’lar “I” ile isimlendirilmezler interface’ler I ile isimlendirilir.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*

Copy Protected by Chetan's WP-Copyprotect.