Строитель(Builder)
KarmanovichDevОпределение с wiki:
- Порождающий шаблон проектирования предоставляет способ создания составного объекта.
Конкретно для меня, не самое понятное и полное объяснение. Даже учитывая, что я понимаю паттерн и использовал его.
Определение с refactoring.guru:
- Порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово. Строитель даёт возможность использовать один и тот же код строительства для получения разных представлений объектов
Вот это совсем другое дело:)
Вообще, все паттерны, которые помогают нам создавать объекты:
1 семейство фабрик(Их правда много. О некоторых из них я расскажу в другой раз)
2 Прототип
3 Тот же строитель, с директором и без
4 и другие...
Призваны забрать на себя ВСЮ сложность создания объекта(это их ответственность).
Если часть сложности будет сокрыта в строителе, часть на самом классе или ком-то ещё, то ответственность за создание такого объекта размазана по коду, и знания о его инвариантах тоже(это плохо).
Вернёмся к определению и разберём его по частям:
- Создание сложных объектов пошагово. Пример сложности:
В конструктор объект ожидает параметры, часть/все из которых нужно где-то получить:
1.1 Они лежат в базе данных
1.2 За ними нужно сходить по сети в другой сервис
1.3 Они могут храниться на диске(на рабочей станции)
1.4 Их можно получить на основании других параметров
2. Вы бы хотели управлять тем, какие из параметров нужно получить в конкретном сценарии использования вашего приложения, а какие из них оставить по умолчанию.
3. И может быть даже, чтобы одни полученные параметры могли влиять на получение других.
Если вы будете делать это в бизнес логике, то непременно получите дублирование знаний(-DRY) создания вашего объекта, как только вам понадобится создать его в другом месте(это плохо).
Так же замешав вашу бизнес логику со сложным созданием объекта, вы назначаете вторую ответственность своей БЛ, такую как “строитель” для сложно конструируемого объекта(в большинстве случаев это будет нарушением SRP(Solid))
Строитель без проблем решает первую и третью проблему инкапсулировав в себе всю сложность различных шагов построения объекта. Вторую решает до тех пор, пока кол-во шагов и/или порядок вызова, не превышает разумных пределов для того, чтобы оставаться в БЛ.
Но для решения второй проблемы у строителя есть элегантное решение, которое называется "директор"(расскажу во второй половине статьи)
Давайте представим, что прямо в бизнес логике мы бы хотели уметь создавать VPS сервер с различными параметрами, который мог бы при создании быть реально запущен на какой-то машине. Такой код с применением паттерна строитель мог бы выглядеть примерно так:

Для реального создания объекта VPS нужно также подключение к системе для создания по API, проверка авторизации, различных прав пользователя, баланса клиента и т. п., но как мы помним, строитель инкапсулирует в себе всю сложность создания объекта. Таким образом, наша БЛ остаётся чистой.
2. Возможность использовать один и тот же код строительства для получения разных представлений объектов:
Представьте, что у нас есть класс HTMLBuilder, у которого есть два метода add_tag, from_builder. Используя эти методы, можно создавать экземпляры одного класса с разным состоянием в итоге.
Состояние объекта(класса) - это атрибуты и переменные экземпляра вашего объекта.
А учитывая текущую реализацию ещё и возможность переиспользовать состояния других экземпляров HTMLBuilder:

Вот мы и подобрались к схеме паттерна(UML):

- Клиент в коде общается строго по интерфейсу абстрактного класса. Ему не известно, какой именно класс наследник он будет использовать в runtime.
- Зная интерфейс взаимодействия строителя, клиент может вызывать любые шаги в любом порядке для создания требуемого объекта.
- Строитель должен сам следить за инвариантами создаваемого объекта, если вдруг окажется, что нельзя трижды вызвать with_domain(ну вдруг такая логика), то строитель должен отреагировать на это, так как задача строителя создать валидный объект в конечном счёте.
Дополнительная информация на boosty:
- Когда стоит использовать паттерн.
- Плюсы, минусы
- Строитель + директор(для ещё более сложных созданий объектов и возможности легко переиспользовать повторяющийся код строителя)
- Реализация кода по UML схеме(боевой пример - похожий объект используется в проде на одном из проектов) + ещё один пример