НЕ МОЛЧИ!!!    Сделай что-нибудь,
чтобы остановить войну России в Украине.
...бойтесь людей равнодушных - именно с их молчаливого согласия происходят все самые ужасные преступления на свете.   ("Репортаж с петлёй на шее")

Шаблон проектирования Builder (Строитель)

Представлен шаблон проектирования Builder  или Строитель, приведены условия его применения и пример реализации на PHP.

Содержание

  • Применение
  • Структура шаблона
  • Реализация
  • Итоги

Применение

Шаблон проектирования Builder, как и другие порождающие, призван отделить от приложения код по созданию объектов. Для этого код по созданию объектов выносится в отдельную иерархию, такое разделение также позволяет создавать объекты по шагам. В итоге один и тот же код используется для создания разных конфигураций или типов объекта. Например, мы создаем какой-то отчет и, в зависимости от предпочтений пользователя или особенностей клиентского устройства, Builder создает его под конкретные требования клиента. Также шаблон Builder весьма полезен при создании достаточно объемных объектов с опциональными свойствами, где громоздкие конструкторы с многочисленными if заменяются конкретными Строителями с использованием повторного кода.

Структура шаблона

Объяснить структуру шаблона будет проще на основе примера, где будем создавать Продукт в различном исполнении. Для этого создаются классы Строителей, каждый из которых умеет создавать свой вариант Продукта. Для унификации строители наследуются от абстрактного класса. Какого использовать Строителя, выбирает Директор. Работает шаблон следующим образом. Клиент создает объект Директор, который с помощью конкретного Строителя создает требуемый Продукт.
В итоге, список участников примера выглядит так.

  • Класс Product — класс объекта, который нужно получить.
  • BuilderAbstract — абстрактный класс, определяющий шаги по созданию частей продукта.
  • Builder1, Builder2 — класс, реализующий создание конкретного продукта.
  • Класс Director — управляет порядком создания конкретных продуктов. Необязательный компонент, он позволяет избавить клиента от процесса управления шагами по созданию продуктов.

А так выглядит их взаимодействие на диаграмме.

                     Director
                          │
             ┌──┴──┐
              ↓                        ↓
          Builder1          Builder2
              │                   │
               ↓                     ↓
           Product             Product
            ├─ Part A1           ├─ Part A2
            └─ Part B1           └─ Part B2

Итак, приступим.

Реализация

Вначале создадим класс Product объекта, который будет создаваться.

<?php

declare(strict_types=1);

class Product
{
    /**
     * @var list<string>
     */
    private array $list = [];

    public function add(string $element): void
    {
        $this->list[] = $element;
    }

    public function getProduct(): void
    {
        foreach ($this->list as $element) {
            echo htmlspecialchars($element, ENT_QUOTES, 'UTF-8') . '<br>';
        }

        echo '<hr>';
    }
}

Далее объект класса Director будет получать на входе Строителя и с его помощью создавать нужный тип продукта.


class Director
{
    public function setConstruct(BuilderAbstract $builder): void
    {
        $builder->buildPartA();
        $builder->buildPartB();
    }
}

Теперь надо создать Строителей. Реализация достаточно стандартная, создается абстрактный строитель с набором нужных методов, который потом реализуется в наследниках. Каждый наследник создает свой вариант Продукта.


abstract class BuilderAbstract
{
    abstract public function buildPartA(): void;

    abstract public function buildPartB(): void;

    abstract public function getResult(): Product;
}



class Builder1 extends BuilderAbstract
{
    private Product $product;

    public function __construct()
    {
        $this->product = new Product();
    }

    public function buildPartA(): void
    {
        $this->product->add('Part A1 of Product');
    }

    public function buildPartB(): void
    {
        $this->product->add('Part B1 of Product');
    }

    public function getResult(): Product
    {
        return $this->product;
    }
}

class Builder2 extends BuilderAbstract
{
    private Product $product;

    public function __construct()
    {
        $this->product = new Product();
    }

    public function buildPartA(): void
    {
        $this->product->add('Part A2 of Product');
    }

    public function buildPartB(): void
    {
        $this->product->add('Part B2 of Product');
    }

    public function getResult(): Product
    {
        return $this->product;
    }
}

Осталось продемонстрировать, как это работает.
Для этого помещаем вышеприведенный код в модуль builder.php. Далее в демонстрационный скрипт index.php помещаем такой код.

<?php

declare(strict_types=1);

require_once 'builder.php';

?>

<!DOCTYPE html>
<html lang="ru">

<head>
    <meta charset="UTF-8">
    <title>Пример работы паттерна Builder</title>
</head>

<body>

<div style="width: 350px;">

    <h3>Пример работы паттерна Builder</h3>

    <?php

    // Создаём Директора.
    $director = new Director();

    // Создаём строителей.
    $builder1 = new Builder1();
    $builder2 = new Builder2();

    // Создаём вариант №1 продукта.
    $director->setConstruct($builder1);

    // Получаем продукт №1.
    $product1 = $builder1->getResult();

    // Выводим продукт №1.
    $product1->getProduct();

    // Создаём вариант №2 продукта.
    $director->setConstruct($builder2);

    // Получаем продукт №2.
    $product2 = $builder2->getResult();

    // Выводим продукт №2.
    $product2->getProduct();

    ?>

</div>

</body>
</html>

При запросе index.php на клиенте получим:

Пример работы паттерна Builder
Part A1 of Product
Part B1 of Product 
-------------------------------------------
Part A2 of Product
Part B2 of Product 
-------------------------------------------

Итоги

Итак, вам был представлен порождающий шаблон проектирования Builder, его назначение и условия, когда его полезно использовать, приведен пример работы.
К достоинствам Строителя следует отнести:

  • отделяет код по созданию объекта от бизнес-логики;
  • повторное использование кода для создания различных объектов;
  • за счет пошагового использования предоставляет более глубокий контроль по созданию объектов.

Из недостатков шаблона можно указать:

  • усложнение кода приложения за счет введения дополнительных классов;
  • создание объекта определяется лишь конкретными пошаговыми возможностями Построителя.

На этом всё.

Успехов в разработке!

Перейти к списку шаблонов проектирования.