Когда бы вы использовали паттерн Строитель?

Что такое общий, примеры из реального мира использования паттерна Builder? Что это вам дает? Почему бы просто не использовать фабричный паттерн?

stackoverflow.com/questions/35238292/… упомянул некоторые API, которые используют шаблон построителя

Alireza Fattahi 06.02.2016 10:30

Ответы от Аарон и Тета действительно информативны. Вот полная статья, связанный с этими ответами.

Diablo 15.05.2017 11:44

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

analytically 20.05.2014 12:43
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
552
3
240 330
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Вы используете его, когда у вас много вариантов, с которыми нужно иметь дело. Подумайте о таких вещах, как 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);

Я забыл прочесть карту «если» реализовал паттерн построителя и был удивлен, увидев конструктор их .. :)

sethu 09.04.2012 15:48

:) Прости за это. Во многих языках принято возвращать self вместо void. Вы можете сделать это в java, но это не очень распространено.

Dustin 16.04.2012 05:17

Пример карты - это просто пример объединения методов.

nogridbag 06.12.2013 08:20

@nogridbag Это было бы ближе к каскадированию методов. Хотя он использует цепочку таким образом, чтобы имитировать каскадирование, поэтому очевидно, что это цепочка, но семантически она ведет себя как каскад.

Didier A. 07.08.2015 05:21
Ответ принят как подходящий

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

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

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

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

Проверьте Шаблон Builder в Википедии.

Для многопоточной задачи нам нужно было создать сложный объект для каждого потока. Объект представляет обрабатываемые данные и может изменяться в зависимости от ввода пользователя.

Можем ли мы использовать вместо этого фабрику? да

Почему мы этого не сделали? Думаю, в Builder больше смысла.

Фабрики используются для создания объектов разных типов, которые относятся к одному и тому же базовому типу (реализуют один и тот же интерфейс или базовый класс).

Строители строят объекты одного и того же типа снова и снова, но конструкция является динамической, поэтому ее можно изменять во время выполнения.

Основываясь на предыдущих ответах (каламбур), отличным примером из реальной жизни является встроенная поддержка Builders в Groovy.

См. Строители в Документация Groovy

Я использовал конструктор в собственной библиотеке обмена сообщениями. Ядро библиотеки получало данные из сети, собирая их с помощью экземпляра Builder, затем, как только Builder решил, что у него есть все необходимое для создания экземпляра сообщения, Builder.GetMessage () конструировал экземпляр сообщения, используя данные, собранные из провод.

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

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

Нитин расширил аналогию с кухней в еще один ответ на этот вопрос.

Pops 01.11.2012 22:21

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

Я не думаю, что это шаблон строителя. StringBuilder - это просто еще одна реализация класса символьного массива (то есть строки), но он учитывает производительность и управление памятью, поскольку строки неизменяемы.

Charles Graham 20.03.2009 09:01

Это абсолютно образец строителя, как и класс StringBuilder в Java. Обратите внимание, как метод append () обоих этих классов возвращает сам StringBuilder, чтобы можно было связать b.append(...).append(...) перед окончательным вызовом toString(). Цитата: infoq.com/articles/internal-dsls-java

pohl 01.04.2011 00:23

@pohl Я не думаю, что это действительно паттерн компоновщика, я бы сказал, что это больше просто свободный интерфейс.

Didier A. 07.08.2015 05:20

«Обратите внимание, как метод append () обоих этих классов возвращает сам StringBuilder» - это не шаблон Builder, это просто удобный интерфейс. Просто часто Строитель ТАКЖЕ использует свободный интерфейс. Строителю не обязательно иметь свободный интерфейс.

bytedev 10.03.2016 15:29

Но обратите внимание, что StringBuilder по своей природе не синхронизирован, в отличие от StringBuffer, который синхронизируется.

Alan Deep 02.10.2017 17:58

Ниже приведены некоторые аргументы в пользу использования шаблона и примера кода в 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, верно? Потому что нет Директорского класса. Мне это кажется почти другим шаблоном, но я согласен, что он очень полезен.

Lino Rosa 24.12.2009 03:02

В этом конкретном примере не было бы лучше удалить логические параметры и иметь возможность сказать new Pizza.Builder(12).cheese().pepperoni().bacon().build();

Fabian Steeg 24.12.2009 04:03

Конечно - я просто использовал логические значения для примера. На самом деле вы, скорее всего, использовали бы конструктор для более сложных параметров.

Aaron 24.12.2009 04:05

@Lino На самом деле это форма GOF Builder.

Aaron 24.12.2009 19:30

@Lino Rosa, это все еще паттерн GOF builder, Pizza - режиссер. :-)

Buhake Sindi 21.09.2011 15:58

Это больше похоже на Свободный интерфейс, чем на шаблон строителя.

Robert Harvey 29.11.2012 02:23

@BuhakeSindi Я полагаю, но это как минимум версия упрощенный конструктора GoF. Например, шаблон GoF, описанный в книге, позволяет создавать совершенно разные продукты, просто заменяя конструктор в клиенте, тем самым повторно используя директор. Мне кажется, что в Bloch Builder вам пришлось бы продублировать «директор» в новом продукте.

Adam Parkin 02.02.2013 02:08

честно говоря, я считаю, что шаблон построителя больше работает с небольшим выигрышем по сравнению с шаблоном javabean

nawfal 18.04.2013 20:40

Я до сих пор не понимаю, зачем вам это использовать. В приведенном выше случае вы можете просто использовать класс, который содержит параметры, которые вам нужно установить (для повышения удобочитаемости), и передать этот параметр фабрике, которая создаст настоящую пиццу.

Daniel Bişar 09.08.2013 18:18

@SACO, я могу сказать вам, почему я использую этот шаблон, чтобы создавать объекты, не имеющие общедоступных сеттеров. Таким образом я могу улучшить управление своей моделью предметной области. Создатель отвечает за то, чтобы я мог создавать только допустимые варианты объекта моей области, в то время как методы этого объекта позволяют потребителям изменять его состояние. Эти методы могут обеспечивать любые необходимые меры защиты и инкапсулировать все изменения логики и состояния, необходимые для выполнения требуемой операции. Если предоставить потребителю установить новое значение для каждого свойства и т. д., Возникнут ошибки / нарушения.

SonOfPirate 11.09.2013 20:41

@Fabian Steeg, я думаю, что люди слишком остро реагируют на более красивые логические сеттеры, имейте в виду, что такие сеттеры не позволяют изменять время выполнения: Pizza.Builder(12).cheese().pepperoni().bacon().build();, вам нужно будет перекомпилировать свой код или иметь ненужную логику, если вам только нужно пицца пепперони. По крайней мере, вы должны также предоставить параметризованные версии, такие как изначально предложенный @Kamikaze Mercenary. Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false)‌​.build();. С другой стороны, мы никогда не проводим модульное тестирование, не так ли?

egallardo 22.11.2013 02:59

В этой реализации нет классов Director или ConcreteBuilder. Это не соответствует шаблону Builder.

Raman Zhylich 16.01.2014 02:28

@JasonC Верно, а какая польза от неизменной пиццы?

Maarten Bodewes 19.04.2014 17:03

@nawfal вы предполагаете, что ваши конструкторы будут для ваших собственных классов. Что, если вам нужно обернуть какой-то другой класс? Сделать что-нибудь на основе параметров, например, создать новые объекты для строительства? Я должен согласиться с тем, что я предпочел бы использовать шаблон javabean в своей собственной работе, но оба они имеют свое место.

frostymarvelous 24.02.2015 09:55

@frostymarvelous, и в этом случае шаблон строителя хорош и того стоит ...

nawfal 01.03.2015 08:25

@nawfal Я думаю, что основная проблема связана с большинством вопросов по программированию. Ответ упрощает проблему, чтобы помочь OP понять. ИМО, это основная причина пресловутого молота.

frostymarvelous 01.03.2015 12:13

телескопический конструктор анти-паттерн возникает, когда увеличение комбинации параметров конструктора объекта приводит к экспоненциальному списку конструкторов.

Dmitry Trifonov 17.03.2015 16:00

@RobertHarvey Как это больше похоже на свободный интерфейс, чем на конструктор? Это класс конструктора учебников, и это разные вещи: stackoverflow.com/a/17940086/2770572

michaelsnowden 25.08.2015 06:24

@doctordoder: У вас может быть конструктор, который не владеет свободно. Свободный строитель требует, чтобы установщик использовал методы returnthis. У вас также могут быть беглые методы, которые не являются конструкторами; см. пример Linq.

Robert Harvey 25.08.2015 07:42

@RobertHarvey Да, именно так. Поэтому я не понимаю, почему вы говорите, что это «больше похоже» на свободный интерфейс, чем на конструктор. Большинство строителей имеют такой код.

michaelsnowden 25.08.2015 08:04

@doctordoder: Я хочу сказать, что конструктор и свободный интерфейс - это две разные концепции. Да, вы можете использовать их вместе, но на этом сходство в значительной степени заканчивается.

Robert Harvey 25.08.2015 08:13

@insidesin: Если бы был путь к напишите конструктор с необязательными аргументами ...

Robert Harvey 25.08.2015 08:27

@egallardo Вы упускаете суть всего шаблона, когда пишете что-то вроде Pizza.Builder (12) .cheese (true) .pepperoni (false) .bacon (false) ‌ .build (); Ключевым преимуществом этого шаблона является то, что вам не нужно передавать значения по умолчанию для необязательных параметров, а можно просто полностью их не указывать, независимо от порядка параметров. Вместо этого вы можете просто написать Pizza.Builder (12) .cheese (true) .build (); Однако: кто в здравом уме заказал бы пиццу только с сыром и ни с чем? ;-)

Kaiserludi 31.08.2015 17:32

@Kaiserludi Я специально говорил об изменениях времени выполнения. Давайте попробуем пример для ясности: предположим, что jsp-страница отправляет список ингредиентов (сыр, помидор и базилик). Ваш код будет выглядеть как Pizza.Builder (12) .cheese (). Tomato (). Basil (). Build (). Теперь, если вам нравится только сыр, ваш код будет выглядеть как Pizza.Builder (12) .cheese (). Build (); Это был бы громоздкий способ создания пиццы, потому что вам нужно было бы перекомпилировать код для каждого заказа ;-)

egallardo 14.10.2015 02:17

@egallardo: Значит, вы говорите мне, что не перестраиваете духовку для каждой пиццы, которую готовите? ;-) Ага, в таком случае, конечно, ты прав.

Kaiserludi 14.10.2015 12:58

Вы пишете: «Проблема в том, что, поскольку объект создается в результате нескольких вызовов, он может находиться в несогласованном состоянии на этапе его создания» - не могли бы вы объяснить или привести пример этого несовместимого состояния? (кроме случаев, когда ваша программа многопоточная)

BornToCode 10.01.2016 17:01

в примере, представленном GOF, есть класс-директор, который использует Строителей с общим интерфейсом для создания представления объекта. в то время как определенные элементы, которые используются для создания этого объекта, инкапсулированы. Таким образом, один и тот же процесс построения может привести к разным представлениям объектов. Это те приключения, которые в приведенном выше примере не предусмотрены. Они упоминают только о том, что класс является директором и продуктом.

Tomasz Mularczyk 26.06.2016 10:44

шаблон построителя GOF и шаблон построителя Bloch - совершенно разные и не связанные между собой шаблоны проектирования. В этом посте обсуждается паттерн Блоха, а не GOF.

Andrew Norman 18.03.2017 01:10

@Aaron Я сомневаюсь, что, как вы сказали, подход с установщиком приведет к «Проблема здесь в том, что поскольку объект создается за несколько вызовов, он может находиться в несогласованном состоянии на полпути в процессе его создания». Но то же самое можно сказать и о Builder pattern, если мы забудем вызвать метод доступа к данным разработчика, например. если мы забудем вызвать .pepperoni(true), объект будет в несогласованном состоянии.

Govinda Sakhare 05.07.2017 09:16

@Aaron Кто-то скопировал и вставил ваш ответ как сообщение в блоге medium.com/@modestofiguereo/…

drum 30.01.2018 08:39

@Aaron: чтобы полагаться на неизменяемый объект и / или объект, который правильно сконструирован и действителен в соответствии с вашими бизнес-правилами, вы должны объявить класс Pizza final. В противном случае кто-то может создать подкласс Pizza и создать его изменяемый экземпляр с недопустимым состоянием.

Filou 28.05.2018 17:30

@Filou отличная идея!

Aaron 29.05.2018 23:10

«Проблема здесь в том, что, поскольку объект создается за несколько вызовов, он может находиться в несовместимом состоянии на полпути в процессе его создания». я не понял, как эта проблема решена в примере. Если я пропущу pizza.setCheese (true); вызов в шаблоне JavaBean, я могу пропустить его Pizza pizza = new Pizza.Builder (12) .build (); в шаблоне построителя, и поведение будет таким же противоречивым. Я что-то упускаю?

Barış Akkurt 06.03.2019 12:51

@Aaron Меня сбивает с толку: «Проблема в том, что, поскольку объект создается за несколько вызовов, он может находиться в несогласованном состоянии на этапе его создания. Это также требует больших дополнительных усилий для обеспечения безопасности потоков». Если один поток создает один объект в одном стеке методов, тогда как безопасность потоков становится здесь проблемой?

nabster 30.12.2019 03:40

@ BarışAkkurt, да, это так. После того, как вы .build () свою пиццу, вы больше не сможете ее изменить, вы можете воссоздать ее с нуля, но это все. Пока вы полностью не построите свой объект, вы не можете его использовать, и в этом вся суть, с сеттерами вы можете сразу начать использовать частично созданный несогласованный объект, теперь представьте, что вы вызываете все сеттеры одного класса в 20 других разных классах - удачи, с помощью Builder вы можете '' т. Моя команда также одержима строителями, пытающимися добиться неизменности с помощью этого повсюду, я могу только сказать, что Kotlin с параметрами по умолчанию + val приходит на помощь.

Aubergine 07.09.2020 02:01

Еще одно преимущество построителя заключается в том, что если у вас есть 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) Объясните, как это отвечает на вопрос.

james.garriss 28.07.2015 19:38

Мне всегда не нравился шаблон 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? Быстрый ответ: не могу, потому что мне нужны строители. И мы совершаем полный круг ...

David Barker 22.11.2014 12:06

total bs, Builder предназначен для создания неизменяемых объектов и может изменить способ строительства в будущем, не затрагивая класс продукта.

Mickey Tin 09.04.2015 18:47

Хм? Вы где-то читали в ответе, что я говорю, для чего предназначен Builder? Это альтернативная точка зрения на вопрос вверху «Когда бы вы использовали шаблон Builder?», Который основан на опыте бесчисленных злоупотреблений этим шаблоном, когда что-то гораздо более простое работает лучше. Все шаблоны полезны, если вы знаете, когда и как их использовать - в этом весь смысл документирования шаблонов в первую очередь! Когда шаблон используется чрезмерно или, что еще хуже, неправильно используется, тогда он становится анти-шаблоном в контексте вашего кода. Увы...

Pavel Lechev 12.04.2015 23:46

Другие вопросы по теме