← Назад к вопросам

Приведи пример реализации Builder

2.0 Middle🔥 182 комментариев
#Архитектура и паттерны#ООП

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отличный вопрос! Паттерн Builder (Строитель) — это порождающий шаблон проектирования, который позволяет создавать сложные объекты пошагово, отделяя процесс конструирования объекта от его представления. Это особенно полезно для объектов со множеством необязательных параметров или сложной логикой инициализации.

Основная идея паттерна

Вместо того чтобы иметь конструктор с десятком параметров или множество перегруженных версий, Builder предоставляет fluent-интерфейс (цепочку методов) для пошаговой настройки объекта. Это делает код создания более читаемым, управляемым и защищенным от ошибок, связанных с неправильным порядком или значением аргументов.

Классический пример: Создание объекта Email

Представим, что нам нужно создать объект электронного письма с множеством полей: отправитель, получатели, тема, тело, вложения и т.д. Не все поля обязательны.

1. Продукт (Product) — Объект, который мы строим

<?php

class Email
{
    private $from;
    private $to = [];
    private $cc = [];
    private $bcc = [];
    private $subject;
    private $body;
    private $attachments = [];

    // Конструктор может быть приватным, чтобы объект создавался только через Builder
    public function __construct() {}

    // Геттеры для получения данных (сеттеры не нужны, объект неизменяемый)
    public function getFrom(): string
    {
        return $this->from;
    }

    public function getTo(): array
    {
        return $this->to;
    }

    // ... остальные геттеры

    /**
     * Внутренний метод для установки свойств.
     * Используется только Builder'ом.
     */
    public function setFrom(string $from): void
    {
        $this->from = $from;
    }

    public function addTo(string $to): void
    {
        $this->to[] = $to;
    }

    // ... аналогичные методы для других свойств
}

2. Интерфейс Строителя (Builder Interface)

Определяет общие шаги конструирования.

<?php

interface EmailBuilderInterface
{
    public function setFrom(string $from): self;
    public function addTo(string $to): self;
    public function addCc(string $cc): self;
    public function addBcc(string $bcc): self;
    public function setSubject(string $subject): self;
    public function setBody(string $body): self;
    public function addAttachment(string $filePath): self;
    public function getEmail(): Email;
}

3. Конкретный Строитель (Concrete Builder)

Реализует интерфейс и предоставляет конкретную реализацию шагов. Он хранит создаваемый объект Email и управляет его состоянием.

<?php

class EmailBuilder implements EmailBuilderInterface
{
    private Email $email;

    public function __construct()
    {
        $this->email = new Email();
    }

    public function setFrom(string $from): self
    {
        $this->email->setFrom($from);
        return $this; // Возврат $this позволяет строить цепочки вызовов
    }

    public function addTo(string $to): self
    {
        $this->email->addTo($to);
        return $this;
    }

    // ... реализация всех остальных методов интерфейса

    public function getEmail(): Email
    {
        // Возвращаем готовый объект
        $builtEmail = $this->email;
        // Опционально: сбрасываем состояние builder'а для нового строительства
        $this->email = new Email();
        return $builtEmail;
    }
}

4. Директор (Director) — опциональный компонент

Определяет порядок шагов конструирования для создания стандартных конфигураций объекта. Позволяет повторно использовать алгоритмы сборки.

<?php

class EmailDirector
{
    private EmailBuilderInterface $builder;

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

    public function buildWelcomeEmail(string $to, string $userName): Email
    {
        return $this->builder
            ->setFrom('welcome@example.com')
            ->addTo($to)
            ->setSubject('Добро пожаловать!')
            ->setBody("Привет, $userName! Мы рады вас видеть.")
            ->getEmail();
    }

    public function buildNotificationEmail(array $recipients, string $message): Email
    {
        $builder = $this->builder->setFrom('notifications@example.com');

        foreach ($recipients as $recipient) {
            $builder->addTo($recipient);
        }

        return $builder
            ->setSubject('Важное уведомление')
            ->setBody($message)
            ->addAttachment('/path/to/rules.pdf')
            ->getEmail();
    }
}

5. Использование (Client Code)

<?php

// Способ 1: Использование Builder напрямую (без Director)
$builder = new EmailBuilder();
$email = $builder
    ->setFrom('my@mail.com')
    ->addTo('friend@mail.com')
    ->addCc('boss@company.com')
    ->setSubject('Отчет')
    ->setBody('Пожалуйста, найдите отчет в приложении.')
    ->addAttachment('/reports/q4.pdf')
    ->getEmail(); // Финальный вызов для получения объекта

echo $email->getSubject(); // "Отчет"

// Способ 2: Использование через Director
$director = new EmailDirector(new EmailBuilder());
$welcomeEmail = $director->buildWelcomeEmail('newuser@mail.com', 'Иван');
$notificationEmail = $director->buildNotificationEmail(['user1@mail.com', 'user2@mail.com'], 'Система будет отключена.');

Ключевые преимущества реализации

  • Читаемость и выразительность: Цепочка методов (->setFrom(...)->addTo(...)) ясно описывает, что создается.
  • Иммутабельность объекта: После вызова getEmail() объект Email "замораживается". Его состояние нельзя изменить через Builder, что повышает надежность.
  • Гибкость: Можно создавать разные представления объекта (например, HtmlEmailBuilder и TextEmailBuilder), реализующие один интерфейс.
  • Контроль над процессом: Метод getEmail() — единственная точка, где объект считается готовым. Здесь можно добавить валидацию (проверить, указан ли получатель).
  • Изоляция сложного кода конструирования: Логика создания сложных конфигураций инкапсулирована в Director.

Вариация: Упрощенный Builder (без интерфейса и директора)

Для менее сложных случаев паттерн можно упростить, объединив Builder и Product, или сделав Builder статическим внутренним классом. Однако классическая реализация, представленная выше, демонстрирует все принципы SOLID: разделение интерфейсов, инверсию зависимостей и открытость/закрытость. Именно такую реализацию я рекомендую использовать в продакшн-коде для решения задач создания сложных объектов.