Czym Jest Wzorzec Projektowy Builder?

W dzisiejszym świecie programowania, gdzie aplikacje stają się coraz bardziej złożone, ważne jest, aby kod był nie tylko funkcjonalny, ale i łatwy do zrozumienia oraz utrzymania. Właśnie tutaj z pomocą przychodzą wzorce projektowe – sprawdzone rozwiązania projektowe, które można wielokrotnie wykorzystywać w różnych kontekstach. Jednym z takich wzorców, który zyskał dużą popularność, jest wzorzec Builder 🏗️.

Czym Jest Wzorzec Builder? 🤔

Wzorzec Builder, znany również jako budowniczy, to kreacyjny wzorzec projektowy, który pozwala na tworzenie złożonych obiektów krok po kroku. Jest to szczególnie przydatne, gdy obiekt musi być skonfigurowany za pomocą wielu opcjonalnych parametrów lub gdy proces tworzenia obiektu jest niezwykle skomplikowany.

Jak Działa Builder? 🏗️

Proces tworzenia obiektu za pomocą wzorca Builder można podzielić na kilka etapów:

  1. Builder: Interfejs Builder określa metody niezbędne do tworzenia różnych części obiektu.
  2. Concrete Builder: Klasy konkretnych budowniczych implementują interfejs Builder i określają konkretną reprezentację obiektu, konstruując i łącząc jego części.
  3. Product: Obiekt, który ma być zbudowany. Wszystkie części obiektu są składane przez obiekt Concrete Builder.
  4. Director: Opcjonalnie, klasa dyrektora może definiować kolejność wywoływania metod budowania, aby stworzyć określone konfiguracje produktu.

Przykłady Zastosowań Builder 📚


Wzorzec Builder znajduje zastosowanie w wielu scenariuszach, na przykład: Budowanie złożonych dokumentów (np. dokumentów tekstowych, plików XML).
Konfiguracja złożonych drzew decyzyjnych lub komponentów GUI.
Zarządzanie konfiguracją skomplikowanych obiektów.

Zacznijmy od definicji klasy bazowej Vehicle i kilku pochodnych klas:

class Vehicle {
    public $type;
    public $wheels;
    public $color;

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

    public function setWheels($wheels) {
        $this->wheels = $wheels;
    }

    public function setColor($color) {
        $this->color = $color;
    }
}

class Car extends Vehicle {
    public function __construct() {
        parent::__construct('Car');
    }
}

class Motorbike extends Vehicle {
    public function __construct() {
        parent::__construct('Motorbike');
    }
}

class Truck extends Vehicle {
    public function __construct() {
        parent::__construct('Truck');
    }
}

Następnie, zdefiniujmy interfejs VehicleBuilder, który określi metody niezbędne do zbudowania obiektu Vehicle, oraz konkretną implementację dla każdego typu pojazdu:

interface VehicleBuilder {
    public function reset();
    public function setWheels($wheels);
    public function setColor($color);
    public function getResult();
}

class CarBuilder implements VehicleBuilder {
    private $car;

    public function reset() {
        $this->car = new Car();
    }

    public function setWheels($wheels) {
        $this->car->setWheels($wheels);
    }

    public function setColor($color) {
        $this->car->setColor($color);
    }

    public function getResult() {
        return $this->car;
    }
}

class MotorbikeBuilder implements VehicleBuilder {
    private $motorbike;

    public function reset() {
        $this->motorbike = new Motorbike();
    }

    public function setWheels($wheels) {
        $this->motorbike->setWheels($wheels);
    }

    public function setColor($color) {
        $this->motorbike->setColor($color);
    }

    public function getResult() {
        return $this->motorbike;
    }
}

class TruckBuilder implements VehicleBuilder {
    private $truck;

    public function reset() {
        $this->truck = new Truck();
    }

    public function setWheels($wheels) {
        $this->truck->setWheels($wheels);
    }

    public function setColor($color) {
        $this->truck->setColor($color);
    }

    public function getResult() {
        return $this->truck;
    }
}

Na koniec, możemy użyć tych budowniczych w naszym kliencie (np., w pliku index.php), aby stworzyć różne rodzaje pojazdów:

function buildVehicle(VehicleBuilder $builder) {
    $builder->reset();
    $builder->setWheels(4);
    $builder->setColor('red');
    return $builder->getResult();
}

$carBuilder = new CarBuilder();
$car = buildVehicle($carBuilder);
print_r($car);

$motorbikeBuilder = new MotorbikeBuilder();
$motorbike = buildVehicle($motorbikeBuilder);
print_r($motorbike);

$truckBuilder = new TruckBuilder();
$truck = buildVehicle($truckBuilder);
print_r($truck);

W tym przykładzie, buildVehicle funkcja przyjmuje budowniczego, który jest używany do zbudowania pojazdu z określoną liczbą kół i kolorem. To tylko podstawowy przykład, który można rozszerzyć o więcej właściwości i złożoności, w zależności od potrzeb.

Wzorzec Builder to potężne narzędzie w arsenale każdego programisty, ułatwiające tworzenie złożonych obiektów. Dzięki swojej elastyczności i zdolności do oddzielania procesu konstrukcji od szczegółów implementacji, Builder znacznie ułatwia zarządzanie kodem i czyni go bardziej czytelnym. Czy zatem jesteś gotowy, aby zbudować swój kod z Builderem? 🏗️🛠️

Przypisy

Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1995). W książce Design Patterns: Elements of Reusable Object-Oriented Software autorzy przedstawiają wzorzec Builder jako sposób na oddzielanie konstrukcji złożonego obiektu od jego reprezentacji. To pozwala na tworzenie różnych reprezentacji tego samego obiektu za pomocą tego samego procesu konstrukcyjnego.

Martin, R. C. (2003). W Clean Code: A Handbook of Agile Software Craftsmanship Robert C. Martin podkreśla znaczenie pisania kodu, który jest łatwy do zrozumienia i utrzymania. Użycie wzorca Builder może pomóc w osiągnięciu tych celów, szczególnie w przypadku klas z dużą liczbą parametrów konstruktora lub kiedy tworzenie instancji klasy jest procesem wieloetapowym.

Bloch, J. (2008). W Effective Java Joshua Bloch szczegółowo omawia implementację niemutowalnych obiektów i wskazuje na wzorzec Builder jako jedną z metod osiągnięcia niemutowalności. Bloch argumentuje, że niemutowalne obiekty są prostsze w użyciu, bezpieczniejsze i mogą pomóc w uniknięciu wielu błędów związanych z równoczesnością.

Moje początki programowania sięgają 2010r. W trakcie wielu projektów zdobywałem doświadczenie, rozwijając nie tylko umiejętności techniczne, ale także kompetencje miękkie. Programuje głównie w PHP i Python.