Паттерны проектирования
13. Паттерн «Строитель» (Builder)
Иногда процесс создания является довольно сложным, состоит из нескольких этапов. Создаваемому объекту нужно передать множество аргументов, часть из которых нужны одним клиентам, но не нужны другим. Фабричный метод с десятью аргументами, девять из которых будут регулярно повторяться, нельзя назвать удачным решением. В этом случае поможет другой порождающий паттерн — «Строитель».
Назначение: строитель отделяет конструирование сложного объекта от его представления, так что в результате одного и того же процесса конструирования могут получаться разные представления.
Причины использования:
Паттерн «Строитель» идеально подходит для ситуаций, когда процесс создания является сложным и состоит из нескольких этапов, при этом одним клиентам нужно устанавливать одни параметры создаваемого объекта, а другим — другие. Строитель может устанавливать разумные значения по умолчанию, позволяя клиентам сосредоточиться лишь на важных для них параметрах.
Использование паттерна «Строитель» позволяет более четко разграничить ответственность между создателем и потребителем объектов, а также делает процесс создания более удобным. Это особенно полезно, если продукт физически или логически является неизменяемым (immutable).
Классическая диаграмма паттерна «Строитель» приведена в верхней части рисунка ниже:
-
Builder определяет интерфейс конструирования продукта по частям;-
Director управляет процессом создания, не зная, какой продукт будет создан в результате;-
ConcreteBuilder — конкретный строитель, который создает известный только ему объект класса Product.Обратите внимание на два момента: наличие наследования и то, что о классе
Product знает только конкретный строитель (ConcreteBuilder). Ни базовый класс строителя, ни его клиент (Director) не знают о типе создаваемого продукта. Это позволяет конкретным строителям формировать совершенно разнородные объекты, но оставляет открытыми многие вопросы:- Кто потребляет созданный продукт?
- Как конкретный строитель узнает об этом потребителе?
- Всегда ли процесс формирования и потребления продуктов должен быть настолько разделён, что продукт конструируется классом
Director, а потребляется непонятно кем?На практике обычно используется более простая разновидность паттерна «Строитель», без наследования и с более явной моделью взаимодействия между участниками (см. в нижней части рисунка). Участники в этом случае те же самые, но ответственность немного меняется:
-
Client (Director) управляет созданием сложного объекта и получает созданный объект путем вызова метода Build. -
Builder отвечает за создание конкретного продукта.Особенности реализации в .NET
1. Использование текучего интерфейса.
Текучий интерфейс предназначен для повышения читабельности кода. Каждый метод возвращает объект, над которым выполняется текущая операция, что позволяет связывать методы в цепочку:
var result = Enumerable.Range(1, 10).Select(n => n + 1).Count();При реализации строителей данная идиома используется постоянно, поскольку позволяет создавать требуемый объект одним оператором. Последним вызывается метод
Build, возвращающий требуемый продукт.2. Методы расширения
Методы расширения позволяют создать видимость того, что мы добавляем операции в существующие типы. В некоторых случаях можно создать набор методов расширения, которые будут играть роль строителя для существующих типов.
Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 11.