Что такое общий, примеры из реального мира использования паттерна Builder? Что это вам дает? Почему бы просто не использовать фабричный паттерн?
Ответы от Аарон и Тета действительно информативны. Вот полная статья, связанный с этими ответами.
Посмотрите мой InnerBuilder, плагин IntelliJ IDEA, который добавляет действие «Строитель» в меню «Создать» (Alt + Insert), которое генерирует внутренний класс построителя, как описано в Эффективной Java github.com/analytical/innerbuilder




Вы используете его, когда у вас много вариантов, с которыми нужно иметь дело. Подумайте о таких вещах, как jmock:
m.expects(once())
.method("testMethod")
.with(eq(1), eq(2))
.returns("someResponse");
Это кажется более естественным и ... возможным.
Также есть построение xml, построение строк и многое другое. Представьте, если бы java.util.Map поставили как строитель. Вы можете сделать что-то вроде этого:
Map<String, Integer> m = new HashMap<String, Integer>()
.put("a", 1)
.put("b", 2)
.put("c", 3);
Я забыл прочесть карту «если» реализовал паттерн построителя и был удивлен, увидев конструктор их .. :)
:) Прости за это. Во многих языках принято возвращать self вместо void. Вы можете сделать это в java, но это не очень распространено.
Пример карты - это просто пример объединения методов.
@nogridbag Это было бы ближе к каскадированию методов. Хотя он использует цепочку таким образом, чтобы имитировать каскадирование, поэтому очевидно, что это цепочка, но семантически она ведет себя как каскад.
Ключевое различие между построителем и фабрикой IMHO заключается в том, что построитель полезен, когда вам нужно сделать много вещей для создания объекта. Например, представьте себе DOM. Вам нужно создать множество узлов и атрибутов, чтобы получить окончательный объект. Фабрика используется, когда фабрика может легко создать весь объект за один вызов метода.
Одним из примеров использования построителя является построение XML-документа. Я использовал эту модель при построении HTML-фрагментов, например, у меня может быть Builder для построения определенного типа таблицы, и он может иметь следующие методы (параметры не показаны):
BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()
Затем этот конструктор выдавал мне HTML-код. Это намного легче читать, чем проходить через большой процедурный метод.
Проверьте Шаблон Builder в Википедии.
Для многопоточной задачи нам нужно было создать сложный объект для каждого потока. Объект представляет обрабатываемые данные и может изменяться в зависимости от ввода пользователя.
Можем ли мы использовать вместо этого фабрику? да
Почему мы этого не сделали? Думаю, в Builder больше смысла.
Фабрики используются для создания объектов разных типов, которые относятся к одному и тому же базовому типу (реализуют один и тот же интерфейс или базовый класс).
Строители строят объекты одного и того же типа снова и снова, но конструкция является динамической, поэтому ее можно изменять во время выполнения.
Основываясь на предыдущих ответах (каламбур), отличным примером из реальной жизни является встроенная поддержка Builders в Groovy.
MarkupBuilderStreamingMarkupBuilderSwingXBuilderСм. Строители в Документация Groovy
Я использовал конструктор в собственной библиотеке обмена сообщениями. Ядро библиотеки получало данные из сети, собирая их с помощью экземпляра Builder, затем, как только Builder решил, что у него есть все необходимое для создания экземпляра сообщения, Builder.GetMessage () конструировал экземпляр сообщения, используя данные, собранные из провод.
Рассмотрим ресторан. Создание «сегодняшней еды» - это фабричный шаблон, потому что вы говорите кухне «принеси мне сегодняшнюю еду», а кухня (фабрика) решает, какой объект создать, на основе скрытых критериев.
Конструктор появляется, если вы заказываете пиццу на заказ. В этом случае официант говорит повару (строителю): «Мне нужна пицца; добавьте в нее сыр, лук и бекон!» Таким образом, построитель предоставляет атрибуты, которые должен иметь сгенерированный объект, но скрывает, как их установить.
Нитин расширил аналогию с кухней в еще один ответ на этот вопрос.
Класс .NET StringBuilder - отличный пример шаблона построителя. В основном он используется для создания строки в несколько этапов. Конечный результат, который вы получаете при выполнении ToString (), всегда является строкой, но создание этой строки зависит от того, какие функции в классе StringBuilder использовались. Подводя итог, можно сказать, что основная идея состоит в том, чтобы создавать сложные объекты и скрывать детали реализации того, как они строятся.
Я не думаю, что это шаблон строителя. StringBuilder - это просто еще одна реализация класса символьного массива (то есть строки), но он учитывает производительность и управление памятью, поскольку строки неизменяемы.
Это абсолютно образец строителя, как и класс StringBuilder в Java. Обратите внимание, как метод append () обоих этих классов возвращает сам StringBuilder, чтобы можно было связать b.append(...).append(...) перед окончательным вызовом toString(). Цитата: infoq.com/articles/internal-dsls-java
@pohl Я не думаю, что это действительно паттерн компоновщика, я бы сказал, что это больше просто свободный интерфейс.
«Обратите внимание, как метод append () обоих этих классов возвращает сам StringBuilder» - это не шаблон Builder, это просто удобный интерфейс. Просто часто Строитель ТАКЖЕ использует свободный интерфейс. Строителю не обязательно иметь свободный интерфейс.
Но обратите внимание, что StringBuilder по своей природе не синхронизирован, в отличие от StringBuffer, который синхронизируется.
Ниже приведены некоторые аргументы в пользу использования шаблона и примера кода в Java, но это реализация шаблона Builder, охватываемого «Бандой четырех» в Шаблоны проектирования. Причины, по которым вы бы использовали его в Java, также применимы к другим языкам программирования.
Как утверждает Джошуа Блох в Эффективная Java, 2-е издание:
The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters.
Все мы в какой-то момент сталкивались с классом со списком конструкторов, каждое добавление которого добавляет новый параметр 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;
}
}
Обратите внимание, что Пицца неизменна, и все значения параметров находятся в одном месте.. Поскольку методы установки Builder возвращают объект Builder, они являются может быть прикован.
Pizza pizza = new Pizza.Builder(12)
.cheese(true)
.pepperoni(true)
.bacon(true)
.build();
В результате получается код, который легко писать и очень легко читать и понимать. В этом примере метод сборки может быть изменен для проверки параметров после того, как они были скопированы из построителя в объект Pizza, и генерировать исключение IllegalStateException, если было предоставлено недопустимое значение параметра. Этот шаблон является гибким, и в будущем к нему легко добавить дополнительные параметры. Это действительно полезно только в том случае, если у вас будет более 4 или 5 параметров для конструктора. Тем не менее, в первую очередь может быть полезно если вы подозреваете, что в будущем можете добавить дополнительные параметры.
Я много позаимствовал на эту тему из книги Джошуа Блоха Эффективная Java, 2-е издание. Чтобы узнать больше об этом шаблоне и других эффективных методах Java Я очень рекомендую это.
Отличается от оригинального конструктора GOF, верно? Потому что нет Директорского класса. Мне это кажется почти другим шаблоном, но я согласен, что он очень полезен.
В этом конкретном примере не было бы лучше удалить логические параметры и иметь возможность сказать new Pizza.Builder(12).cheese().pepperoni().bacon().build();
Конечно - я просто использовал логические значения для примера. На самом деле вы, скорее всего, использовали бы конструктор для более сложных параметров.
@Lino На самом деле это форма GOF Builder.
@Lino Rosa, это все еще паттерн GOF builder, Pizza - режиссер. :-)
Это больше похоже на Свободный интерфейс, чем на шаблон строителя.
@BuhakeSindi Я полагаю, но это как минимум версия упрощенный конструктора GoF. Например, шаблон GoF, описанный в книге, позволяет создавать совершенно разные продукты, просто заменяя конструктор в клиенте, тем самым повторно используя директор. Мне кажется, что в Bloch Builder вам пришлось бы продублировать «директор» в новом продукте.
честно говоря, я считаю, что шаблон построителя больше работает с небольшим выигрышем по сравнению с шаблоном javabean
Я до сих пор не понимаю, зачем вам это использовать. В приведенном выше случае вы можете просто использовать класс, который содержит параметры, которые вам нужно установить (для повышения удобочитаемости), и передать этот параметр фабрике, которая создаст настоящую пиццу.
@SACO, я могу сказать вам, почему я использую этот шаблон, чтобы создавать объекты, не имеющие общедоступных сеттеров. Таким образом я могу улучшить управление своей моделью предметной области. Создатель отвечает за то, чтобы я мог создавать только допустимые варианты объекта моей области, в то время как методы этого объекта позволяют потребителям изменять его состояние. Эти методы могут обеспечивать любые необходимые меры защиты и инкапсулировать все изменения логики и состояния, необходимые для выполнения требуемой операции. Если предоставить потребителю установить новое значение для каждого свойства и т. д., Возникнут ошибки / нарушения.
@Fabian Steeg, я думаю, что люди слишком остро реагируют на более красивые логические сеттеры, имейте в виду, что такие сеттеры не позволяют изменять время выполнения: Pizza.Builder(12).cheese().pepperoni().bacon().build();, вам нужно будет перекомпилировать свой код или иметь ненужную логику, если вам только нужно пицца пепперони. По крайней мере, вы должны также предоставить параметризованные версии, такие как изначально предложенный @Kamikaze Mercenary. Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false).build();. С другой стороны, мы никогда не проводим модульное тестирование, не так ли?
В этой реализации нет классов Director или ConcreteBuilder. Это не соответствует шаблону Builder.
@JasonC Верно, а какая польза от неизменной пиццы?
@nawfal вы предполагаете, что ваши конструкторы будут для ваших собственных классов. Что, если вам нужно обернуть какой-то другой класс? Сделать что-нибудь на основе параметров, например, создать новые объекты для строительства? Я должен согласиться с тем, что я предпочел бы использовать шаблон javabean в своей собственной работе, но оба они имеют свое место.
@frostymarvelous, и в этом случае шаблон строителя хорош и того стоит ...
@nawfal Я думаю, что основная проблема связана с большинством вопросов по программированию. Ответ упрощает проблему, чтобы помочь OP понять. ИМО, это основная причина пресловутого молота.
телескопический конструктор анти-паттерн возникает, когда увеличение комбинации параметров конструктора объекта приводит к экспоненциальному списку конструкторов.
@RobertHarvey Как это больше похоже на свободный интерфейс, чем на конструктор? Это класс конструктора учебников, и это разные вещи: stackoverflow.com/a/17940086/2770572
@doctordoder: У вас может быть конструктор, который не владеет свободно. Свободный строитель требует, чтобы установщик использовал методы returnthis. У вас также могут быть беглые методы, которые не являются конструкторами; см. пример Linq.
@RobertHarvey Да, именно так. Поэтому я не понимаю, почему вы говорите, что это «больше похоже» на свободный интерфейс, чем на конструктор. Большинство строителей имеют такой код.
@doctordoder: Я хочу сказать, что конструктор и свободный интерфейс - это две разные концепции. Да, вы можете использовать их вместе, но на этом сходство в значительной степени заканчивается.
@insidesin: Если бы был путь к напишите конструктор с необязательными аргументами ...
@egallardo Вы упускаете суть всего шаблона, когда пишете что-то вроде Pizza.Builder (12) .cheese (true) .pepperoni (false) .bacon (false) .build (); Ключевым преимуществом этого шаблона является то, что вам не нужно передавать значения по умолчанию для необязательных параметров, а можно просто полностью их не указывать, независимо от порядка параметров. Вместо этого вы можете просто написать Pizza.Builder (12) .cheese (true) .build (); Однако: кто в здравом уме заказал бы пиццу только с сыром и ни с чем? ;-)
@Kaiserludi Я специально говорил об изменениях времени выполнения. Давайте попробуем пример для ясности: предположим, что jsp-страница отправляет список ингредиентов (сыр, помидор и базилик). Ваш код будет выглядеть как Pizza.Builder (12) .cheese (). Tomato (). Basil (). Build (). Теперь, если вам нравится только сыр, ваш код будет выглядеть как Pizza.Builder (12) .cheese (). Build (); Это был бы громоздкий способ создания пиццы, потому что вам нужно было бы перекомпилировать код для каждого заказа ;-)
@egallardo: Значит, вы говорите мне, что не перестраиваете духовку для каждой пиццы, которую готовите? ;-) Ага, в таком случае, конечно, ты прав.
Вы пишете: «Проблема в том, что, поскольку объект создается в результате нескольких вызовов, он может находиться в несогласованном состоянии на этапе его создания» - не могли бы вы объяснить или привести пример этого несовместимого состояния? (кроме случаев, когда ваша программа многопоточная)
в примере, представленном GOF, есть класс-директор, который использует Строителей с общим интерфейсом для создания представления объекта. в то время как определенные элементы, которые используются для создания этого объекта, инкапсулированы. Таким образом, один и тот же процесс построения может привести к разным представлениям объектов. Это те приключения, которые в приведенном выше примере не предусмотрены. Они упоминают только о том, что класс является директором и продуктом.
шаблон построителя GOF и шаблон построителя Bloch - совершенно разные и не связанные между собой шаблоны проектирования. В этом посте обсуждается паттерн Блоха, а не GOF.
@Aaron Я сомневаюсь, что, как вы сказали, подход с установщиком приведет к «Проблема здесь в том, что поскольку объект создается за несколько вызовов, он может находиться в несогласованном состоянии на полпути в процессе его создания». Но то же самое можно сказать и о Builder pattern, если мы забудем вызвать метод доступа к данным разработчика, например. если мы забудем вызвать .pepperoni(true), объект будет в несогласованном состоянии.
@Aaron Кто-то скопировал и вставил ваш ответ как сообщение в блоге medium.com/@modestofiguereo/…
@Aaron: чтобы полагаться на неизменяемый объект и / или объект, который правильно сконструирован и действителен в соответствии с вашими бизнес-правилами, вы должны объявить класс Pizza final. В противном случае кто-то может создать подкласс Pizza и создать его изменяемый экземпляр с недопустимым состоянием.
@Filou отличная идея!
«Проблема здесь в том, что, поскольку объект создается за несколько вызовов, он может находиться в несовместимом состоянии на полпути в процессе его создания». я не понял, как эта проблема решена в примере. Если я пропущу pizza.setCheese (true); вызов в шаблоне JavaBean, я могу пропустить его Pizza pizza = new Pizza.Builder (12) .build (); в шаблоне построителя, и поведение будет таким же противоречивым. Я что-то упускаю?
@Aaron Меня сбивает с толку: «Проблема в том, что, поскольку объект создается за несколько вызовов, он может находиться в несогласованном состоянии на этапе его создания. Это также требует больших дополнительных усилий для обеспечения безопасности потоков». Если один поток создает один объект в одном стеке методов, тогда как безопасность потоков становится здесь проблемой?
@ BarışAkkurt, да, это так. После того, как вы .build () свою пиццу, вы больше не сможете ее изменить, вы можете воссоздать ее с нуля, но это все. Пока вы полностью не построите свой объект, вы не можете его использовать, и в этом вся суть, с сеттерами вы можете сразу начать использовать частично созданный несогласованный объект, теперь представьте, что вы вызываете все сеттеры одного класса в 20 других разных классах - удачи, с помощью Builder вы можете '' т. Моя команда также одержима строителями, пытающимися добиться неизменности с помощью этого повсюду, я могу только сказать, что Kotlin с параметрами по умолчанию + val приходит на помощь.
Еще одно преимущество построителя заключается в том, что если у вас есть Factory, в вашем коде все еще есть некоторая взаимосвязь, потому что чтобы Factory работал, он должен знать все объекты, которые он может создать. Если вы добавите еще один объект, который может быть создан, вам придется изменить фабричный класс, чтобы включить его. Это происходит и в Абстрактной фабрике.
С другой стороны, с помощью строителя вам просто нужно создать новый конкретный конструктор для этого нового класса. Класс директора останется прежним, потому что он получает построитель в конструкторе.
Также есть много вкусов строителя. Наемник-камикадзе дает еще один.
Проходя через платформу Microsoft MVC, я подумал о шаблоне построителя. Я наткнулся на шаблон в классе ControllerBuilder. Этот класс должен возвращать фабричный класс контроллера, который затем используется для создания конкретного контроллера.
Преимущество, которое я вижу в использовании шаблона построителя, заключается в том, что вы можете создать собственную фабрику и подключить ее к фреймворку.
@Tetha, может быть ресторан (Framework), которым управляет итальянский парень, где подают пиццу. Для приготовления пиццы итальянец (Строитель объектов) использует Оуэна (Фабрика) с базой для пиццы (базовый класс).
Теперь индийский парень берет на себя управление рестораном у итальянца. Индийский ресторан (Framework) серверы dosa вместо пиццы. Для приготовления доса индийский парень (конструктор объектов) использует сковороду (фабрику) с майдой (базовый класс).
Если вы посмотрите на сценарий, еда будет другой, еда будет другой, но в одном и том же ресторане (в рамках одной и той же системы). Ресторан должен быть построен таким образом, чтобы он мог поддерживать китайскую, мексиканскую или любую другую кухню. Конструктор объектов внутри фреймворка упрощает создание плагина для той кухни, которую вы хотите. Например
class RestaurantObjectBuilder
{
IFactory _factory = new DefaultFoodFactory();
//This can be used when you want to plugin the
public void SetFoodFactory(IFactory customFactory)
{
_factory = customFactory;
}
public IFactory GetFoodFactory()
{
return _factory;
}
}
Когда я хотел использовать стандартный XMLGregorianCalendar для своего XML для сортировки объектов DateTime в Java, я слышал много комментариев о том, насколько тяжелым и громоздким было его использование. Я пытался контролировать поля XML в структурах xs: datetime для управления часовым поясом, миллисекундами и т. д.
Поэтому я разработал утилиту для создания XML-григорианского календаря из GregorianCalendar или java.util.Date.
Из-за того, где я работаю, мне не разрешено делиться им в Интернете без закона, но вот пример того, как клиент его использует. Он абстрагирует детали и фильтрует некоторые реализации XMLGregorianCalendar, которые меньше используются для xs: datetime.
XMLGregorianCalendarBuilder builder = XMLGregorianCalendarBuilder.newInstance(jdkDate);
XMLGregorianCalendar xmlCalendar = builder.excludeMillis().excludeOffset().build();
Конечно, этот шаблон больше похож на фильтр, поскольку он устанавливает для полей в xmlCalendar значение undefined, поэтому они исключаются, он по-прежнему «строит» его. Я легко добавил в конструктор другие параметры для создания структур xs: date и xs: time, а также для управления смещениями часовых поясов, когда это необходимо.
Если вы когда-нибудь видели код, который создает и использует XMLGregorianCalendar, вы бы увидели, насколько это упростило манипулирование.
/// <summary>
/// Builder
/// </summary>
public interface IWebRequestBuilder
{
IWebRequestBuilder BuildHost(string host);
IWebRequestBuilder BuildPort(int port);
IWebRequestBuilder BuildPath(string path);
IWebRequestBuilder BuildQuery(string query);
IWebRequestBuilder BuildScheme(string scheme);
IWebRequestBuilder BuildTimeout(int timeout);
WebRequest Build();
}
/// <summary>
/// ConcreteBuilder #1
/// </summary>
public class HttpWebRequestBuilder : IWebRequestBuilder
{
private string _host;
private string _path = string.Empty;
private string _query = string.Empty;
private string _scheme = "http";
private int _port = 80;
private int _timeout = -1;
public IWebRequestBuilder BuildHost(string host)
{
_host = host;
return this;
}
public IWebRequestBuilder BuildPort(int port)
{
_port = port;
return this;
}
public IWebRequestBuilder BuildPath(string path)
{
_path = path;
return this;
}
public IWebRequestBuilder BuildQuery(string query)
{
_query = query;
return this;
}
public IWebRequestBuilder BuildScheme(string scheme)
{
_scheme = scheme;
return this;
}
public IWebRequestBuilder BuildTimeout(int timeout)
{
_timeout = timeout;
return this;
}
protected virtual void BeforeBuild(HttpWebRequest httpWebRequest) {
}
public WebRequest Build()
{
var uri = _scheme + "://" + _host + ":" + _port + "/" + _path + "?" + _query;
var httpWebRequest = WebRequest.CreateHttp(uri);
httpWebRequest.Timeout = _timeout;
BeforeBuild(httpWebRequest);
return httpWebRequest;
}
}
/// <summary>
/// ConcreteBuilder #2
/// </summary>
public class ProxyHttpWebRequestBuilder : HttpWebRequestBuilder
{
private string _proxy = null;
public ProxyHttpWebRequestBuilder(string proxy)
{
_proxy = proxy;
}
protected override void BeforeBuild(HttpWebRequest httpWebRequest)
{
httpWebRequest.Proxy = new WebProxy(_proxy);
}
}
/// <summary>
/// Director
/// </summary>
public class SearchRequest
{
private IWebRequestBuilder _requestBuilder;
public SearchRequest(IWebRequestBuilder requestBuilder)
{
_requestBuilder = requestBuilder;
}
public WebRequest Construct(string searchQuery)
{
return _requestBuilder
.BuildHost("ajax.googleapis.com")
.BuildPort(80)
.BuildPath("ajax/services/search/web")
.BuildQuery("v=1.0&q = " + HttpUtility.UrlEncode(searchQuery))
.BuildScheme("http")
.BuildTimeout(-1)
.Build();
}
public string GetResults(string searchQuery) {
var request = Construct(searchQuery);
var resp = request.GetResponse();
using (StreamReader stream = new StreamReader(resp.GetResponseStream()))
{
return stream.ReadToEnd();
}
}
}
class Program
{
/// <summary>
/// Inside both requests the same SearchRequest.Construct(string) method is used.
/// But finally different HttpWebRequest objects are built.
/// </summary>
static void Main(string[] args)
{
var request1 = new SearchRequest(new HttpWebRequestBuilder());
var results1 = request1.GetResults("IBM");
Console.WriteLine(results1);
var request2 = new SearchRequest(new ProxyHttpWebRequestBuilder("localhost:80"));
var results2 = request2.GetResults("IBM");
Console.WriteLine(results2);
}
}
Вы можете улучшить свои ответы двумя способами: 1) Сделайте это SSCCE. 2) Объясните, как это отвечает на вопрос.
Мне всегда не нравился шаблон Builder как нечто громоздкое, навязчивое и очень часто использованное менее опытными программистами. Это шаблон, который имеет смысл только в том случае, если вам нужно собрать объект из некоторых данных, для которых требуется этап постинициализации (т.е. после того, как все данные будут собраны, сделайте с ними что-нибудь). Вместо этого в 99% случаев построители просто используются для инициализации членов класса.
В таких случаях гораздо лучше просто объявить установщики типа withXyz(...) внутри класса и заставить их возвращать ссылку на себя.
Учти это:
public class Complex {
private String first;
private String second;
private String third;
public String getFirst(){
return first;
}
public void setFirst(String first){
this.first=first;
}
...
public Complex withFirst(String first){
this.first=first;
return this;
}
public Complex withSecond(String second){
this.second=second;
return this;
}
public Complex withThird(String third){
this.third=third;
return this;
}
}
Complex complex = new Complex()
.withFirst("first value")
.withSecond("second value")
.withThird("third value");
Теперь у нас есть аккуратный единственный класс, который управляет своей собственной инициализацией и выполняет почти ту же работу, что и построитель, за исключением того, что он намного элегантнее.
Я просто решил, что хочу создать свой сложный XML-документ как JSON. Во-первых, как мне узнать, что класс «Complex» в первую очередь способен доставлять продукт в формате XML, и как мне изменить его для создания объекта JSONable? Быстрый ответ: не могу, потому что мне нужны строители. И мы совершаем полный круг ...
total bs, Builder предназначен для создания неизменяемых объектов и может изменить способ строительства в будущем, не затрагивая класс продукта.
Хм? Вы где-то читали в ответе, что я говорю, для чего предназначен Builder? Это альтернативная точка зрения на вопрос вверху «Когда бы вы использовали шаблон Builder?», Который основан на опыте бесчисленных злоупотреблений этим шаблоном, когда что-то гораздо более простое работает лучше. Все шаблоны полезны, если вы знаете, когда и как их использовать - в этом весь смысл документирования шаблонов в первую очередь! Когда шаблон используется чрезмерно или, что еще хуже, неправильно используется, тогда он становится анти-шаблоном в контексте вашего кода. Увы...
stackoverflow.com/questions/35238292/… упомянул некоторые API, которые используют шаблон построителя