Вопрос-ответ

When would you use the Builder Pattern? [closed]

Когда бы вы использовали шаблон Builder?

Каковы некоторые распространенные, реальные примеры использования шаблона Builder? Что это дает вам? Почему бы просто не использовать шаблон Factory?

Переведено автоматически
Ответ 1

Ниже приведены некоторые доводы в пользу использования шаблона и примера кода в Java, но это реализация шаблона Builder, описанного Gang of Four в Шаблонах проектирования. Причины, по которым вы хотели бы использовать его в Java, также применимы и к другим языкам программирования.

Как утверждает Джошуа Блох в Эффективной Java, 2-е издание:


Шаблон builder является хорошим выбором при проектировании классов, конструкторы которых или статические фабрики будут иметь более нескольких параметров.


Все мы в какой-то момент сталкивались с классом со списком конструкторов, где каждое добавление добавляет новый параметр option:

Pizza(int size) { ... }        
Pizza(int size, boolean cheese) { ... }
Pizza(int size, boolean cheese, boolean pepperoni) { ... }
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }

Это называется шаблоном телескопического конструктора. Проблема с этим шаблоном заключается в том, что, когда конструкторы содержат 4 или 5 параметров, становится трудно запомнить требуемый порядок параметров, а также какой конкретный конструктор вам может понадобиться в данной ситуации.

У вас есть альтернатива шаблону телескопического конструктора - это шаблон JavaBean, в котором вы вызываете конструктор с обязательными параметрами, а затем вызываете любые необязательные установщики после:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);

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

Лучшей альтернативой является использование шаблона Builder .

public class Pizza {
private int size;
private boolean cheese;
private boolean pepperoni;
private boolean bacon;

public static class Builder {
//required
private final int size;

//optional
private boolean cheese = false;
private boolean pepperoni = false;
private boolean bacon = false;

public Builder(int size) {
this.size = size;
}

public Builder cheese(boolean value) {
cheese = value;
return this;
}

public Builder pepperoni(boolean value) {
pepperoni = value;
return this;
}

public Builder bacon(boolean value) {
bacon = value;
return this;
}

public Pizza build() {
return new Pizza(this);
}
}

private Pizza(Builder builder) {
size = builder.size;
cheese = builder.cheese;
pepperoni = builder.pepperoni;
bacon = builder.bacon;
}
}

Обратите внимание, что Pizza неизменяем и что все значения параметров находятся в одном месте. Поскольку методы-установщики Builder возвращают объект Builder, они могут быть объединены в цепочку.

Pizza pizza = new Pizza.Builder(12)
.cheese(true)
.pepperoni(true)
.bacon(true)
.build();

В результате получается код, который легко писать и очень легко читать и понимать. В этом примере метод сборки может быть изменен для проверки параметров после того, как они были скопированы из builder в объект Pizza, и выдавать исключение IllegalStateException, если было указано недопустимое значение параметра. Этот шаблон гибкий, и к нему легко добавить дополнительные параметры в будущем. На самом деле он полезен только в том случае, если у вас будет более 4 или 5 параметров для конструктора. Тем не менее, это может быть полезно в первую очередь, если вы подозреваете, что в будущем вам придется добавлять дополнительные параметры.

Я многое позаимствовал по этой теме из книги Джошуа Блоха "Эффективная Java, 2-е издание". Чтобы узнать больше об этом шаблоне и других эффективных практиках Java, я настоятельно рекомендую его.

Ответ 2

Рассмотрим ресторан. Создание "блюда на сегодня" - это фабричный шаблон, потому что вы говорите кухне "приготовьте мне сегодняшнее блюдо", и кухня (фабрика) решает, какой объект сгенерировать, на основе скрытых критериев.

Конструктор появляется, если вы заказываете пиццу на заказ. В этом случае официант говорит шеф-повару (builder): "Мне нужна пицца; добавьте в нее сыр, лук и бекон!" Таким образом, builder предоставляет атрибуты, которые должны быть у сгенерированного объекта, но скрывает, как их установить.

Ответ 3

Ключевое различие между builder и factory, ИМХО, заключается в том, что builder полезен, когда вам нужно сделать много вещей для создания объекта. Например, представьте DOM. Вам нужно создать множество узлов и атрибутов, чтобы получить конечный объект. Фабрика используется, когда фабрика может легко создать весь объект за один вызов метода.

Одним из примеров использования builder является создание XML-документа, я использовал эту модель при создании HTML-фрагментов, например, у меня может быть Builder для создания определенного типа таблицы, и у него могут быть следующие методы (параметры не показаны):

BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()

Затем этот builder выдал бы мне HTML. Это гораздо проще читать, чем переходить к большому процедурному методу.

Ознакомьтесь с шаблоном Builder в Википедии.

Ответ 4

Класс .NET StringBuilder - отличный пример шаблона builder. В основном он используется для создания строки в несколько шагов. Конечный результат, который вы получаете при выполнении toString(), всегда является строкой, но создание этой строки зависит от того, какие функции в классе StringBuilder использовались. Подводя итог, основная идея заключается в создании сложных объектов и скрытии деталей реализации того, как они создаются.

java