В чем разница между исходным шаблоном Builder от GoF и "измененным шаблоном GoF Builder" Джошуа Блоха?
Каков пересмотренный шаблон строителя?
Ответ 1
Образец GoF фокусируется на абстрагировании шагов построения, так что, изменяя построитель, вы можете получать разные результаты, в то время как "пересмотренный строитель" нацеливается на проблему ненужной сложности, добавляемую несколькими конструкторами. Таким образом, шаблон GoF больше связан с абстракцией, а пересмотренный patter - более простота (IMO).
Посмотрите примеры в http://en.wikipedia.org/wiki/Builder_pattern и http://rwhansen.blogspot.com/2007/07/theres-builder-pattern-that-joshua.html, и это должно быть совершенно ясно.
Ответ 2
Примечание re Ответ Микко: Пример Хансена имеет несколько проблем - или, по крайней мере, отличия от версии Блоха, хотя я считаю, что версия Блоха превосходит.
В частности:
Сначала поля Widget
устанавливаются в Builder.build()
, а не в конструкторе Widget
, и поэтому не являются (и не могут быть) final
. Widget
считается неизменным, но нет ничего, что могло бы помешать другому программисту продвигаться вперед и позже добавлять сеттеры.
Во-вторых, комментарии в методе построения Hansen говорят, что "предварительная проверка подлинности идет здесь". Блох (EJ 2ed. С .15) говорит:
Очень важно проверить [инварианты] после копирования параметров от строителя к объекту и проверить их на поля объектов, а не на поля построителя (пункт 39).
Если вы перешли к пункту 39 (стр. 185), вы увидите рассуждения:
[Это] защищает класс от изменений параметров из другого потока во время "окна уязвимости" между моментом проверки параметров и временем их копирования.
Поля в Widget
являются неизменяемыми и не требуют какого-либо оборонительного копирования, но тем не менее безопаснее просто придерживаться правильного шаблона, если кто-то приходит и добавляет Date
или массив или некоторые изменчивые Collection
позже. (Он также защищает от другого потока, изменяя Builder
в середине вызова build()
, но это довольно узкое окно безопасности, поэтому, вероятно, лучше всего сделать так, чтобы Builder
не делились между потоками.)
Более блоховская версия будет:
public class Widget {
public static class Builder {
private String name;
private String model;
private String serialNumber;
private double price;
private String manufacturer;
public Builder( String name, double price ) {
this.name = name;
this.price = price;
}
public Widget build() {
Widget result = new Widget(this);
// *Post*-creation validation here
return result;
}
public Builder manufacturer( String value ) {
this.manufacturer = value;
return this;
}
public Builder serialNumber( String value ) {
this.serialNumber = value;
return this;
}
public Builder model( String value ) {
this.model = value;
return this;
}
}
private final String name;
private final String model;
private final String serialNumber;
private final double price;
private final String manufacturer;
/**
* Creates an immutable widget instance.
*/
private Widget( Builder b ) {
this.name = b.name;
this.price = b.price;
this.model = b.model;
this.serialNumber = b.serialNumber;
this.manufacturer = b.manufacturer;
}
// ... etc. ...
}
Все поля Widget
теперь final
, и все они проверяются после построения.