Press "Enter" to skip to content

Posts tagged as “Builder”

Builder (PHP)

Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code. The intent of the Builder design pattern is to separate the construction of a complex object from its representation.

Benefit:

  • You can construct objects step-by-step, defer construction steps or run steps recursively.
  • You can reuse the same construction code when building various representations of products. Single Responsibility Principle.
  • You can isolate complex construction code from the business logic of the product.

Disadvantage:

  • The overall complexity of the code increases since the pattern requires creating multiple new classes.

Why?

Imagine a complex object that requires different attributes, step-by-step initialization of many fields, and nested objects. Such initialization code usually comes with lots of parameters inside a constructor.

For example, a house is a complex object to implement, some houses have swimming pools, some houses have garages…etc. Let see the following approach:

  • we can create a big constructor in the base House class with all possible parameters that control the house object. But end up most of the parameters will be unused, making the constructor calls really ugly.
  • we have the base House class, extend the base House class and create a set of subclasses to cover all combinations of the parameters. But eventually, end up with a lot of subclasses.

Sample Code:

<?php

namespace JSTechRoad\Builder;

interface Builder
{
    public function producePartA(): void;

    public function producePartB(): void;

    public function producePartC(): void;
}

/**
 * implementations of the building steps.
 */
class ConcreteBuilder1 implements Builder
{
    private $product;

    /**
     * A new builder instance should contain a blank product object
     */
    public function __construct()
    {
        $this->reset();
    }

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

    /**
     * All production steps work with the same product instance.
     */
    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;
    }
}

/**
 * Use the Builder pattern only when your products are very
 * complex and require extensive configuration. 
 */
class Product1
{
    public $parts = [];

    public function listParts(): void
    {
        echo "Product parts: " . implode(', ', $this->parts) . "\n\n";
    }
}

/**
 * The Director is only responsible for executing the 
 * building steps in a particular sequence. It is helpful 
 * when producing products according to a
 * specific order or configuration. Strictly speaking,
 * the Director class is
 * optional, since the client can control builders directly.
 */
class Director
{
    private $builder;

    /**
     * The Director works with any builder instance 
     * that the client code passes to it.
     */
    public function setBuilder(Builder $builder): void
    {
        $this->builder = $builder;
    }

    /**
     * The Director can construct several product 
     * variations using the same building steps.
     */
    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 "Standard basic product:\n";
    $director->buildMinimalViableProduct();
    $builder->getProduct()->listParts();

    echo "Standard full featured product:\n";
    $director->buildFullFeaturedProduct();
    $builder->getProduct()->listParts();

    // The Builder pattern can be used without a Director class.
    echo "Custom product:\n";
    $builder->producePartA();
    $builder->producePartC();
    $builder->getProduct()->listParts();
}

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