Creational — Oluşturucu Desenler

Builder

İnşaatçı (Kurucu) Deseni

Amaç: Karmaşık bir nesnenin adım adım oluşturulmasını sağlamak; aynı oluşturma süreciyle farklı görünümlerde nesneler üretebilmek.

1

Basit Anlatım

Teknik terim yok — günlük hayattan bir benzetme

Bir burger dükkânında sipariş verdiğinizi düşünün. Garson size tek seferde "Ekmek, köfte, peynir, marul, domates, turşu, soğan, sos istiyor musunuz, evet mi hayır mı?" diye 10 soruyu aynı anda sormaz.

Bunun yerine adım adım ilerlersiniz: "Ekmek tam buğday olsun. Köfte duble. Peynir ekle. Soğan istemiyorum." Sonunda "Tamam, hazırla" dersiniz ve burgeriniz gelir.

Bir de "usta" var

Menüde hazır seçenekler de var: "Klasik Burger" dediğinizde usta, aynı adımları her seferinde aynı sırayla uygular. Siz adımları tek tek söylemek zorunda kalmazsınız.

Bir ev inşaatı da böyledir: önce temel, sonra duvarlar, sonra çatı, en son boya. Havuzlu da yapabilirsiniz, havuzsuz da. Aynı süreç, farklı sonuçlar.

Builder şunu der: "Karmaşık bir şeyi tek hamlede değil, anlaşılır adımlarla kur; sonunda bitmiş ürünü teslim al."
2

Teknik Anlamı

Bir yazılımcının bakış açısıyla

Builder, nesne oluşturma kodunu nesnenin kendisinden ayırarak ayrı bir builder nesnesine taşır. Nesne, bir dizi adım (metot çağrısı) ile yapılandırılır ve en sonda Build() ile elde edilir.

Çözdüğü problem: Teleskopik constructor

new Pizza("büyük", true, false, true, null, 2, false) gibi bir çağrıda hangi parametrenin ne olduğunu anlamak imkânsızdır. Opsiyonel parametre sayısı arttıkça constructor'lar çoğalır ve okunabilirlik çöker.

Katılımcılar

.NET'ten tanıdık örnekler

Avantajlar

Dezavantajlar

3

C# ile Uygulama

Adım adım, çalışan kod örneği

Senaryo: Bir e-posta mesajı oluşturuyoruz. Alıcı ve konu zorunlu; CC, ek, öncelik gibi alanlar opsiyonel.

Adım 1 Product: değiştirilemez (immutable) Email sınıfı

Constructor internal olduğu için dışarıdan ancak builder ile oluşturulabilir.

public class Email
{
    public string To { get; }
    public string Subject { get; }
    public string Body { get; }
    public IReadOnlyList<string> Cc { get; }
    public IReadOnlyList<string> Attachments { get; }
    public bool IsUrgent { get; }

    internal Email(string to, string subject, string body, List<string> cc,
                   List<string> attachments, bool isUrgent)
    {
        To = to; Subject = subject; Body = body;
        Cc = cc; Attachments = attachments; IsUrgent = isUrgent;
    }

    public override string ToString() =>
        $"Kime: {To} | CC: {string.Join(", ", Cc)} | Konu: {Subject}" +
        $"{(IsUrgent ? " [ACİL]" : "")} | Ekler: {Attachments.Count} adet";
}

Adım 2 Fluent Builder

Her metot this döndürdüğü için çağrılar zincirlenebilir. Build() son kontrolleri yapar.

public class EmailBuilder
{
    private string? _to;
    private string? _subject;
    private string _body = "";
    private readonly List<string> _cc = new();
    private readonly List<string> _attachments = new();
    private bool _urgent;

    public EmailBuilder To(string address)      { _to = address; return this; }
    public EmailBuilder Subject(string subject) { _subject = subject; return this; }
    public EmailBuilder Body(string body)       { _body = body; return this; }
    public EmailBuilder Cc(string address)      { _cc.Add(address); return this; }
    public EmailBuilder Attach(string file)     { _attachments.Add(file); return this; }
    public EmailBuilder Urgent()                { _urgent = true; return this; }

    public Email Build()
    {
        if (string.IsNullOrWhiteSpace(_to))
            throw new InvalidOperationException("Alıcı zorunludur.");
        if (string.IsNullOrWhiteSpace(_subject))
            throw new InvalidOperationException("Konu zorunludur.");
        return new Email(_to, _subject, _body, new(_cc), new(_attachments), _urgent);
    }
}

Adım 3 Director: hazır tarifler

Sık kullanılan konfigürasyonları tek bir yerde toplar.

public static class EmailDirector
{
    public static Email MonthlyReport(string manager) =>
        new EmailBuilder()
            .To(manager)
            .Cc("finans@firma.com")
            .Subject("Aylık Rapor")
            .Body("Rapor ektedir.")
            .Attach("rapor.xlsx")
            .Build();
}

Adım 4 Kullanım

Tüm parçaları bir araya getiren istemci (client) kodu:

var email = new EmailBuilder()
    .To("ayse@firma.com")
    .Subject("Sunucu çöktü")
    .Cc("devops@firma.com")
    .Cc("cto@firma.com")
    .Attach("log.txt")
    .Urgent()
    .Build();
Console.WriteLine(email);

Console.WriteLine(EmailDirector.MonthlyReport("mehmet@firma.com"));

try
{
    new EmailBuilder().Subject("Alıcısız mesaj").Build();
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Hata: {ex.Message}");
}

Konsol çıktısı

Kime: ayse@firma.com | CC: devops@firma.com, cto@firma.com | Konu: Sunucu çöktü [ACİL] | Ekler: 1 adet
Kime: mehmet@firma.com | CC: finans@firma.com | Konu: Aylık Rapor | Ekler: 1 adet
Hata: Alıcı zorunludur.

Kodda dikkat edilmesi gerekenler

Özetle: Builder, çok parçalı nesneleri okunabilir adımlarla oluşturur ve sonunda doğrulanmış, tutarlı bir nesne teslim eder.
← Ana sayfaya dön