2016-04-05 1 views
3

Итак, в знаменитой книге Effective Java она представляет шаблон Builder, где вы можете создать внутренний статический класс Builder для создания экземпляра класса. Книга предлагает следующий дизайн класса:Является ли вложенный класс Builder действительно необходимым, как описано в «Эффективной Java»?

public class Example { 
    private int a; 
    private int b; 

    public static class Builder() { 
     private int a; 
     private int b; 

     public Builder a(int a) { 
      this.a = a; 
      return this; 
     } 

     public Builder b(int b) { 
      this.b = b; 
      return this; 
     } 

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

    private Example(Builder builder) { 
     this.a = builder.a; 
     this.b = builder.b; 
    } 
} 

Однако я не в состоянии понять, почему мы действительно нуждаемся внутренний Builder class? Вышеприведенный код имеет повторяющиеся строки для деклараций полей (int a, b), это стало бы довольно беспорядочным, если бы у нас было больше полей.

Почему бы не просто избавиться от класса Builder и позволить классу Example взять на себя все set методы, которые были в классе Builder?

Так инстанцирует Example, он стал бы Example e = new Example().a(3).b.(3); вместо Example e = new Example.Builder.a(3).b(3).build();


ПРИМЕЧАНИЕ: Книга предлагает этот шаблон для классов, которые имеют длинный список параметров, которые необходимо установить.

+1

Это наиболее полезно для изготовления неизменяемых объектов. – shmosel

+0

В некоторых случаях сложной инициализации ваши значения уже должны быть установлены. Кроме того, использование методов 'a()' и 'b()' изменяет объект 'Example'. – EpicPandaForce

+0

Builder не нужен для этого случая и фактически делает код намного более подробным. –

ответ

8

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

Есть несколько причин, почему вы хотите использовать построитель:

  • Для построения сложных неизменяемых объектов. Неизменяемые объекты должны иметь окончательные (или логически окончательные) поля, поэтому они должны быть установлены во время построения.

    Предположим, что у вас есть N полей, но вы хотите явно указать некоторые из них в определенных случаях использования. Вам понадобится до 2^N конструкторов, чтобы охватить все случаи, известные как «телескопирование», поскольку длина списка параметров становится длиннее и длиннее. Конструктор позволяет вам моделировать необязательные параметры: если вы не хотите устанавливать этот параметр, не вызывайте этот метод setter.

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

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

+0

Хорошая точка в названии метода именования, позволяющая вывести значение параметра. – KevinO

+0

Спасибо Энди, однако я правильно говорю, что ваш второй пункт может быть достигнут без шаблона построения? (с обычными сеттерами). Итак, шаблон построения полезен только тогда, когда я хочу, чтобы мой класс был неизменным? –

+1

Я имею в виду, что можно указать методы набора «Builder-like» для класса (который достигает вашей второй точки), но это сделает класс изменчивым. Таким образом, шаблон строителя выгоден ** только **, если я хочу, чтобы мой класс был ** неизменным ** с выгодой для вашего второго пункта. –

1

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

Внутренний класс строителя позволяет поэтапно инициализировать поля.

Как указывали другие, это относится и к неизменяемым объектам. Поля не обязательно должны быть окончательными; они будут эффективными, если в внешнем классе не будут установлены сеттеры.

Строитель может аккумулировать параметры более эффективно, чем прямая конструкция. Рассмотрим StringBuilder. Он выделяет временный буфер для накопления частичных результатов. Операция «построить» в ее случае - toString().

Наконец, могут быть вещи, которые вы просто не можете сделать в конструкторе класса. Если вам нужно передать значение супер-конструктору, но это значение не является частью аргументов для вашего конструктора, возможно, это будет невозможно, поскольку сначала вы должны позвонить super(), и вы не сможете создать аргумент (s) как простое выражение внутри вызова super(...). BoxLayout приходит на ум. Вы передаете JPanel в конструктор BoxLayout. Вы передаете макет конструктору JPanel. Курица и яйцо. И этот код не разрешен, потому что this еще не построен.

class X extends JPanel { 
    X() { 
     super(new BoxLayout(this)); // Error: Cannot use "this" yet 
    } 
} 

К счастью, JPanel не является неизменным; вы можете установить макет после строительства.

+0

Нет: если поля внешнего класса являются окончательными, вы можете предоставить их через явные параметры конструктора. –

+0

@ AndyTurner - извините, я имел в виду постепенное строительство. Я отредактирую, чтобы сделать более ясным. – AJNeufeld

+0

@ AndyTurner. Вы правы, однако книга предполагает, что этот шаблон полезен только для тех объектов, которые имеют список параметров, которые нельзя просто передать как параметры конструктора. Извините за путаницу, я должен был прояснить вопрос –

1

Обоснование для сложных классов. Обратите внимание на то, что Builder объекта возвращает себя, таким образом, можно сделать цепочки, такими как:

Example exp = Example.Builder().a(5).b(10).build(); 

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

+0

Спасибо, Кевин, это было проницательно, +1! –

Смежные вопросы