Effective Java
Effective Javaを読み始めたのでメモなど
項目1 コンストラクタの代わりにstaticファクトリーメソッドを検討する
普通にクラスのインスタンスを生成するならばコンストラクタを使用するけれど、コンストラクタよりもstaticファクトリーメソッドを使ったほうが良いケースがある。
staticファクトリーメソッドの利点
public class Hoge { // コンストラクタ // "Hoge"クラスなのでコンストラクタの名前はHogeになってしまう public Hoge(int i, int j, String str) { } // staticファクトリーメソッド // 好きなメソッド名を付けることができたり、同じ引数のメソッドだけど違う結果がほしい時にも便利 public static factoryMethod1(int i, int j, String str) { return XXX; } public static factoryMethod2(int i, int j, String str) { return XXX; }
staticファクトリーメソッドなら、新たなオブジェクトを生成することが必要ない。
- immutableなオブジェクトを生成したいときにも良い
- パフォーマンスが大幅に向上することも
パラメータ化された型のインスタンス生成の面倒さを低減できる。
// "<String, List<String>>"って書くのがめんどい Map<String, List<String>> map = new HashMap<String, List<String>>(); // Java 7ならダイアモンド構文がある! Map<String, List<String>> map = new HashMap<>(); // staticファクトリーメソッドを使えば、上の冗長だった文が簡潔に書ける public static <K, V> HashMap<K, V> newInstance() { return new HashMap<K, V>(); } Map<String, List<String>> map = HashMap.newInstance();
staticファクトリーメソッドの欠点
newInstance()ってのはよく見る気がするし、メソッドチェーンぽくインスタンス生成できるのは良さげだと思った。
項目2 数多くのコンストラクタパラメータに直面した時にはビルダーを検討する
- テレスコーピングコンストラクタ・パターンだと、引数の種類が多すぎると対処するのが大変。
public class Facts { private final int calories; // 必須 private final int fat; // オプション private final int sodium; // オプション // コンストラクタがパターンごとに必要になってしまう // また、引数の順序が難しい public Facts(int calories) { this(calories, 0, 0); } public Facts(int calories, int fat) { this(calories, fat, 0); } public Facts(int calories, int fat, int sodium) { this.calories = calories; this.fat = fat; this.sodium = sodium; } } // colaインスタンスの生成 Facts cola1 = new Facts(100, 50, 20); // {calories, fat, sodium} = {100, 50, 20} Facts cola2 = new Facts(100, 50); // {calories, fat, sodium} = {100, 50, 0} Facts cola3 = new Facts(100); // {calories, fat, sodium} = {100, 0, 0} ...
- Java Beansパターンだと、生成の過程で不整合な状態ができてしまうことがある。
public class Facts { private int calories = 0; private int fat = 0; private int sodium = 0; // コンストラクタは空で引数もなし public Facts() {} // Setterで処理 public void setCalories(int val) { this.calories = val; } public void setFat(int val) { this.fat = val; } public void setSodium(int val) { this.sodium = val; } } // colaインスタンスを生成し、その後Setterで状態を与える Facts cola = new Facts(); cola.setCalories(100); cola.setFat(40); ...
- ビルダーパターンで書くと
public class Facts { private final int calories; // 必須 private final int fat; // オプション private final int sodium; // オプション public static class Builder { // 必須 private final int calories; // オプションはデフォルト値で初期化しておく private int fat = 0; private int sodium = 0; // 必須パラメータはコンストラクタで代入 public Builder(int calories) { this.calories = calories; } // オプションパラメータは好きなだけ public Builder fat(int val) { this.calories = val; return this; } public Builder sodium(int val) { this.sodium = val; return this; } public Facts build() { return new Facts(this); } } private Facts(Builder builder) { this.calories = builder.calories; this.fat = builder.fat; this.sodium = builder.sodium; } } // colaインスタンスの生成 Facts cola1 = new Facts.builder(100).fat(50).sodium(20).build(); Facts cola2 = new Facts.builder(100).fat(50).build();
ビルダーパターンはいい感じだけど、記述量が結構増えてしまうので引数が多い状況に使うのが良さそう
テレスコーピングコンストラクタ・パターンやJavaBeansパターンよりはかなり安全。