Builder збирає складний об'єкт поетапно, крок за кроком, замість того щоб передавати все одразу у конструктор.
Яку проблему вирішує
Коли об'єкт має багато опцій, конструктор розростається до десятка аргументів, половина з яких опціональні. Виклик new Report('Sales', $rows, true, false, null, ...) стає нечитабельним, а порядок аргументів - джерелом помилок.
Як вирішує
Окремий білдер тримає проміжний стан і надає fluent-методи для кожної опції. Кожен метод повертає $this, тож виклики ланцюжаться, а фінальний build() збирає готовий об'єкт. Читач бачить, що саме налаштовується, бо кожен крок має ім'я.
class Report
{
public function __construct(
public string $title,
public array $rows,
public bool $withTotals,
) {}
}
class ReportBuilder
{
private string $title = 'Report';
private array $rows = [];
private bool $withTotals = false;
public function title(string $title): self
{
$this->title = $title;
return $this;
}
public function rows(array $rows): self
{
$this->rows = $rows;
return $this;
}
public function withTotals(bool $value = true): self
{
$this->withTotals = $value;
return $this;
}
public function build(): Report
{
return new Report($this->title, $this->rows, $this->withTotals);
}
}
$report = (new ReportBuilder())
->title('Sales')
->rows([['product' => 'Book', 'sum' => 100]])
->withTotals()
->build();
Де застосовувати
- Складні об'єкти з багатьма опціями: запити, PDF, email, об'ємні DTO.
- Коли потрібно зручно створювати різні конфігурації одного об'єкта.
- У самому Laravel ідея Builder всюди:
Query Builder, Mail, Notification, Http - це ланцюжки методів, що поетапно конфігурують об'єкт перед виконанням.
Плюси та мінуси
- + Прибирає довгі конструктори, код виклику стає самодокументованим.
- + Дозволяє валідувати чи нормалізувати дані всередині
build().
- − Додатковий клас на кожен об'єкт.
- − Надлишковий для об'єкта з двома-трьома полями: звичайний конструктор або іменовані аргументи PHP 8 (
new Report(title: 'Sales')) зрозуміліші за окремий білдер.