2-2:多くのコンストラクタパラメータに直面したときにはビルダーパターンを検討する
staticファクトリや、コンストラクタでは、多くのパラメータを持つときにうまく対応できない
テレスコーピングコンストラクタパターン・・・
必須パラメータだけを受け取るコンストラクタ、1からすべてのオプションを受け取るコンストラクタをそれぞれ作る。
→ クライアントのコードを書くのが困難になり、そのコードを読むのはさらに困難
安全性は高いが、可読性低い
JavaBeansパターン・・・
オブジェクトを生成するためにパラメータなしのコンストラクタを呼び出し、必要とされる各パラメータと関心のあるオプションパラメータを設定するために
セッターメソッドを呼び出す。
→ その生成過程の途中で不整合な状態にあるかもしれない&JavaBeansがクラスを不変にする可能性を排除すること。という欠点ある。
可読性は高いが、安全性は低い
両者の安全性、可読性を兼ね備えた選択肢が、"Builderパターン"
Builderパターン・・・
望むオブジェクトを直接生成する代わりに、クライアントは必須パラメータをすべて持つコンストラクタと、
関心のあるオプションパラメータを個別に設定するために、ビルダーオブジェクトに対してセッターのようなメソッドを呼び出す。
そして最後に典型的には不変オブジェクトを生成するために、パラメータを持たないbuildメソッドを呼び出す。
Builderパターンは、クラス階層に適している。
★Builderパターンは、4つ以上のパラメータがある場合にだけ使うべき。
/**
* Effective Java 2章:オブジェクトの生成と消滅
* 項目2 多くのコンストラクタパラメータに直面した時にはビルダーを検討する
*
* ビルダーパターン例
*
* クライアント呼び出し例
* NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
* .calories(100).sodium(35).cardohydrate(27).build();
*/
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// 必須パラメータ
private final int servingSize;
private final int servings;
// オプションパラメータ - デフォルト値に初期化
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
/**
* Effective Java 2章:オブジェクトの生成と消滅
* 項目2 多くのコンストラクタパラメータに直面した時にはビルダーを検討する
*
* クライアント呼び出し例
* NyPizza pizza = new NyPizza.Builder(SMALL)
* .addTopping(SAUSAGE).addTopping(ONION).build();
*
* Calzone calzone = new Calzone.Builder()
* .addTopping(HAM).sauceInside().build();
*/
public abstract class Pizza {
public enum Topping {
HAM, MUSHROOM, ONION, PEPPER, SAUSAGE
}
final Set<Topping> toppings;
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone();
}
}
public class NyPizza extends Pizza {
public enum Size {
SMALL, MEDIUM, LARGE
}
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override
public NyPizza build() {
return new NyPizza(this);
}
@Override
protected Builder self() {
return this;
}
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
public class Calzone extends Pizza {
private final boolean sauceInside;
public static class Builder extends Pizza.Builder<Builder> {
private boolean sauceInside = false;
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override
public Calzone build() {
return new Calzone(this);
}
@Override
protected Builder self() {
return this;
}
}
private Calzone(Builder builder) {
super(builder);
sauceInside = builder.sauceInside;
}
}