Teknik Detaylar

Builder Tasarım Deseni Nedir?

← Teknik Detaylar
2021-08-03 · 7 dk okuma
Builder Tasarım Deseni Nedir?
Bu yazıyı yapay zekâ ile tartış
Sayfayı kopyala

Bu yazı Design Patterns/Tasarım Desenleri Nedir? başlıklı yazı dizisinin bir parçasıdır.

Bu içerik ağırlıklı olarak refactoring.guru sitesindeki içeriğin tercümesi ve derlenmesinden oluşturulmuştur.

Tüm tasarım desenleri ya da diğer adıyla tasarım kalıplarına yönelik ayrıntılı içeriklere yazının sonundaki bağlantılardan ulaşabilirsiniz.

💡 Özet (TL;DR):

  • Amaç: Karmaşık nesnelerin adım adım (step-by-step) kurulmasını sağlar. Aynı kurucu kod ile farklı tür ve gösterimdeki nesneleri üretmeye olanak tanır.
  • Kilit Yapılar: Builder Arayüzü (Builder), Somut Builder (ConcreteBuilder), Yönetici (Director), Ürün (Product).
  • Motto: Separate the construction of a complex object from its representation (Karmaşık nesnenin kurulumunu temsili biçiminden ayır).

Builder Tasarım Deseninin Amacı

Builder (Kurucu/İnşaatçı), karmaşık nesnelerin adım adım inşa edilmesini sağlayan yaratımsal (creational) bir tasarım desenidir. Bu desen, aynı kurulum adımlarını kullanarak farklı tiplerde veya konfigürasyonlarda nesneler oluşturmanıza olanak tanır.


Sorun

Çok sayıda alan (field) ve iç içe geçmiş nesne içeren karmaşık bir yapıyı kurmaya çalıştığınızı düşünün. Genellikle bu tip nesnelerin başlatma kodları, onlarca parametre alan devasa kurucu metotlara (constructor) veya doğrudan istemci kodun içerisine dağılmış uzun atamalara dönüşür.

Builder sorunu

Örneğin, bir House (Ev) nesnesini modelleyelim. Temel bir ev için duvarlar, kapılar, pencereler ve bir çatı yeterlidir. Ancak ya yüzme havuzlu, bahçeli, akıllı ev sistemine sahip lüks bir ev kurmak isterseniz?

İlk çözüm, House sınıfını miras alan alt sınıflar (örn: HouseWithGarage, HouseWithPool) oluşturmaktır. Fakat her ek özellikte bu sınıf hiyerarşisi kontrolsüzce büyüyecektir.

İkinci çözüm, temel House sınıfı içine tüm olası parametreleri alan devasa tek bir kurucu metot yazmaktır:

Builder constructor sorunu

Bu yaklaşıma Teleskobik Kurucu (Telescoping Constructor) denir. Parametrelerin çoğu isteğe bağlı (optional) olduğu için, 10 evden 9'unda havuz parametresi boş (null) geçilecek ve kod okunabilirliği ciddi şekilde bozulacaktır.


Çözüm

Builder deseni, nesne kurulum kodunu kendi sınıfından çıkarıp Builder adı verilen bağımsız nesnelere taşımayı önerir.

Builder çözümü

Nesne kurulum süreci adımlara bölünür (buildWalls, buildDoors, buildPool vb.). İstediğiniz ürünü oluşturmak için builder nesnesi üzerindeki adımları sırayla çağırırsınız. En büyük avantajı, sadece o ev için gerekli olan adımları çağırmanızın yeterli olmasıdır.

Farklı gösterimler (ahşap ev vs. taş ev) üretmek için aynı builder arayüzünü uygulayan alternatif somut builder sınıfları yazabilirsiniz:

Farklı builder varyasyonları


Director (Yönetici) Sınıfı

Süreci daha da optimize etmek için, builder adımlarını belirli şablonlara göre çağıran bir Director (Yönetici) sınıfı oluşturabilirsiniz. Director, adımların hangi sırada ve nasıl çağrılacağını bilir.

Director konsepti

Director sınıfı projenizde zorunlu değildir; istemci kod adımları doğrudan kendisi de tetikleyebilir. Ancak Director, yaygın kurulum şablonlarını tek bir yerde toplayıp tekrar kullanılabilir kılmak için faydalıdır.


Gerçek Hayat Senaryosu: SQL Sorgu Oluşturucu (SQL Query Builder)

Farklı SQL lehçelerine göre (MySQL, PostgreSQL) sorgu inşa eden akıcı (fluent) bir PHP Query Builder örneği:

// 1. Ortak Builder Arayüzü
interface SQLQueryBuilder {
    public function select(string $table, array $fields): SQLQueryBuilder;
    public function where(string $field, string $value, string $operator = '='): SQLQueryBuilder;
    public function limit(int $start, int $offset): SQLQueryBuilder;
    public function getSQL(): string;
}

// 2. MySQL için Somut Builder
class MySQLQueryBuilder implements SQLQueryBuilder {
    private stdClass $query;

    public function __construct() {
        $this->reset();
    }

    private function reset(): void {
        $this->query = new stdClass();
    }

    public function select(string $table, array $fields): SQLQueryBuilder {
        $this->query->select = "SELECT " . implode(", ", $fields) . " FROM " . $table;
        return $this; // Fluent interface için nesneyi dönüyoruz
    }

    public function where(string $field, string $value, string $operator = '='): SQLQueryBuilder {
        $this->query->where[] = "$field $operator '$value'";
        return $this;
    }

    public function limit(int $start, int $offset): SQLQueryBuilder {
        $this->query->limit = "LIMIT " . $start . ", " . $offset;
        return $this;
    }

    public function getSQL(): string {
        $sql = $this->query->select;
        if (!empty($this->query->where)) {
            $sql .= " WHERE " . implode(" AND ", $this->query->where);
        }
        if (isset($this->query->limit)) {
            $sql .= " " . $this->query->limit;
        }
        $sql .= ";";
        $this->reset();
        return $sql;
    }
}

İstemci kodu bu builder'ı zincirleme çağrılarla (method chaining) çok temiz şekilde kullanabilir:

$query = (new MySQLQueryBuilder())
    ->select("users", ["id", "username", "email"])
    ->where("status", "active")
    ->limit(0, 10)
    ->getSQL();

echo $query;
// Çıktı: SELECT id, username, email FROM users WHERE status = 'active' LIMIT 0, 10;

Builder vs Abstract Factory vs Factory Method

KriterBuilderAbstract FactoryFactory Method
Üretim BiçimiNesneyi adım adım (step-by-step) kurar.İlişkili nesne ailelerini bir bütün olarak üretir.Tek bir somut nesneyi hemen üretir.
Çıktı KontrolüSon aşamaya kadar yarım kalmış ürünü dışarıya vermez.Nesneyi tek çağrıda tamamen hazır olarak döner.Nesneyi tek çağrıda tamamen hazır olarak döner.
Kullanım AmacıKurulumu karmaşık olan tekil nesneler.Birbiriyle uyumlu çoklu ürün aileleri.Ürünün türetilme işini alt sınıfa delege etmek.

Uygulanabilirlik

  • Teleskobik Kurucudan Kaçınmak: Bir sınıfın yapıcı metodu çok fazla parametre alıyorsa ve bu parametrelerin çoğu isteğe bağlıysa kullanın.
  • Farklı Ürün Varyasyonları: Kodunuzun, benzer adımlardan geçerek farklı tipte (örn: ahşap/taş ev) nesneler üretmesini istediğinizde kullanın.
  • Kompozit (Composite) Nesneler: Ağaç yapısındaki gibi karmaşık nesne yapılarını adım adım inşa etmek istediğinizde kullanın.

Diğer Tasarım Desenleri ile İlişkisi

  • Genellikle tasarımlar basit olan Factory Method ile başlar ve gereksinimler karmaşıklaştıkça Builder desenine evrilir.
  • Abstract Factory nesneyi anında teslim ederken, Builder nesneyi teslim etmeden önce kurulum adımları koşturmanıza olanak tanır.
  • Kurulum adımlarını kontrol eden Director sınıfları çoğunlukla Singleton olarak tasarlanır.

Builder Tasarım Deseni Kod Örnekleri

Örnek PHP Kodu

<?php

namespace RefactoringGuru\Builder\Conceptual;

interface Builder
{
    public function producePartA(): void;
    public function producePartB(): void;
    public function producePartC(): void;
}

class ConcreteBuilder1 implements Builder
{
    private Product1 $product;

    public function __construct()
    {
        $this->reset();
    }

    public function reset(): void
    {
        $this->product = new Product1();
    }

    public function producePartA(): void
    {
        $this->product->parts[] = "PartA1";
    }

    public function producePartB(): void
    {
        $this->product->parts[] = "PartB1";
    }

    public function producePartC(): void
    {
        $this->product->parts[] = "PartC1";
    }

    public function getProduct(): Product1
    {
        $result = $this->product;
        $this->reset();

        return $result;
    }
}

class Product1
{
    public array $parts = [];

    public function listParts(): void
    {
        echo "Ürün parçaları: " . implode(', ', $this->parts) . "\n\n";
    }
}

class Director
{
    private Builder $builder;

    public function setBuilder(Builder $builder): void
    {
        $this->builder = $builder;
    }

    public function buildMinimalViableProduct(): void
    {
        $this->builder->producePartA();
    }

    public function buildFullFeaturedProduct(): void
    {
        $this->builder->producePartA();
        $this->builder->producePartB();
        $this->builder->producePartC();
    }
}

function clientCode(Director $director)
{
    $builder = new ConcreteBuilder1();
    $director->setBuilder($builder);

    echo "Standart Başlangıç Ürünü:\n";
    $director->buildMinimalViableProduct();
    $builder->getProduct()->listParts();

    echo "Standart Tam Donanımlı Ürün:\n";
    $director->buildFullFeaturedProduct();
    $builder->getProduct()->listParts();

    echo "Özel Tasarım Ürün (Director Olmadan):\n";
    $builder->producePartA();
    $builder->producePartC();
    $builder->getProduct()->listParts();
}

$director = new Director();
clientCode($director);

Örnek Python Kodu

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any, List

class Builder(ABC):
    @property
    @abstractmethod
    def product(self) -> None:
        pass

    @abstractmethod
    def produce_part_a(self) -> None:
        pass

    @abstractmethod
    def produce_part_b(self) -> None:
        pass

    @abstractmethod
    def produce_part_c(self) -> None:
        pass


class ConcreteBuilder1(Builder):
    def __init__(self) -> None:
        self.reset()

    def reset(self) -> None:
        self._product = Product1()

    @property
    def product(self) -> Product1:
        product = self._product
        self.reset()
        return product

    def produce_part_a(self) -> None:
        self._product.add("PartA1")

    def produce_part_b(self) -> None:
        self._product.add("PartB1")

    def produce_part_c(self) -> None:
        self._product.add("PartC1")


class Product1():
    def __init__(self) -> None:
        self.parts: List[Any] = []

    def add(self, part: Any) -> None:
        self.parts.append(part)

    def list_parts(self) -> None:
        print(f"Ürün parçaları: {', '.join(self.parts)}", end="")


class Director:
    def __init__(self) -> None:
        self._builder = None

    @property
    def builder(self) -> Builder:
        return self._builder

    @builder.setter
    def builder(self, builder: Builder) -> None:
        self._builder = builder

    def build_minimal_viable_product(self) -> None:
        self.builder.produce_part_a()

    def build_full_featured_product(self) -> None:
        self.builder.produce_part_a()
        self.builder.produce_part_b()
        self.builder.produce_part_c()


if __name__ == "__main__":
    director = Director()
    builder = ConcreteBuilder1()
    director.builder = builder

    print("Standart Başlangıç Ürünü: ")
    director.build_minimal_viable_product()
    builder.product.list_parts()

    print("\n")

    print("Standart Tam Donanımlı Ürün: ")
    director.build_full_featured_product()
    builder.product.list_parts()

    print("\n")

    print("Özel Tasarım Ürün (Director Olmadan): ")
    builder.produce_part_a()
    builder.produce_part_b()
    builder.product.list_parts()

Sıkça Sorulan Sorular (FAQ)

Director sınıfı kullanmak zorunlu mudur?

Halbuki hayır. İstemci kod builder metotlarını doğrudan kendisi sırayla çağırabilir. Director sınıfı, kodda aynı kurulum adımlarının tekrar tekrar yazılmasını engellemek ve yaygın kurulum senaryolarını tek merkezde toplamak için kullanılan opsiyonel bir yapıdır.

Akıcı Arayüz (Fluent Interface / Method Chaining) ile Builder deseni arasındaki ilişki nedir?

Akıcı arayüz, builder adımlarının return $this; (veya Python'da return self) döndürmesiyle sağlanır. Bu yaklaşım, adımları zincirleme şekilde ($builder->addA()->addB()->build()) çağırarak kodun okunabilirliğini ve yazım hızını ciddi derecede artırır.

Builder deseni hangi durumlarda aşırı mühendislik (over-engineering) sayılır?

Nesneniz basitse, en fazla 2-3 parametre alıyorsa ve gelecekte genişleme ihtimali yoksa Builder deseni uygulamak gereksiz kod kalabalığı yaratacaktır. Bu durumlarda standart bir yapıcı metot (constructor) veya basit bir fabrika (factory) işinizi görecektir.


Diğer Tasarım Kalıpları/Design Patterns

Oluşumsal Kalıplar (Creational Patterns)

Factory Method, Abstract Factory, Builder, Prototype, Singleton

Yapısal Kalıplar (Structural Patterns)

Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

Davranışsal Kalıplar (Behavioral Patterns)

Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor