Замечание по поводу Ответ Микко : Пример Хансена имеет несколько проблем - или, по крайней мере, отличия от версии Блоха, хотя я считаю, что версия Блоха лучше.
В частности:
Во-первых, поля Widget
задаются в Builder.build()
, а не в конструкторе Widget
, и поэтому не являются (и не могут быть) final
. Widget
считается неизменным, но ничто не мешает другому программисту прийти и добавить сеттеры позже.
Во-вторых, комментарии в методе сборки Хансена говорят: «Проверка перед созданием начинается здесь». Блох (EJ 2ed. Стр.15) говорит:
Очень важно, чтобы [инварианты] были проверены после копирования параметров из компоновщика в объект и чтобы они проверялись в полях объекта, а не в полях компоновщика (элемент 39).
Если вы перейдете к пункту 39 (стр. 185), вы увидите причину:
[Это] защищает класс от изменений параметров из другого потока во время «окна уязвимости» между временем проверки параметров и временем их копирования.
Поля в Widget
являются неизменяемыми и не требуют защитного копирования, но, тем не менее, безопаснее просто придерживаться правильного шаблона, если кто-то придет и добавит Date
или массив или какой-либо изменяемый Collection
позже. (Он также защищает от другого потока, изменяющего Builder
в середине вызова на build()
, но это довольно узкое окно безопасности, поэтому лучше всего просто убедиться, что Builder
s не разделены между потоками. )
Более блохоподобная версия будет:
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
, и все они проверяются после создания.