Jの衝動書き日記

さらりーまんSEの日記でございます。

Effective Javaの読書メモ 第2章 オブジェクトの生成と消滅 その2

 Effective Javaの読書メモ。今回はオブジェクト作成の話。

 いわゆるデータ型のクラスを作るとき、いつも悩むのがインスタンス変数がやたら多い場合どうするかということだ。スレッド制御が楽になるためimmutable(不変)なクラスにしたいが、引数がやたらに多いコンストラクタが出来上がってしまう。一方setterを用意すれば作成は楽になるけど、immutableなクラスにはできない。変数の設定忘れもよくある間違いだ。

 コンストラクタの引数は少なく、setterは使えるが設定し終えたら変更不可なクラス。そんなクラスが作れればいいのだが、無理である。

 ……と諦めていたところに眼から鱗が落ちたのが今回の話である。

項目2 数多くのコンストラクタパラメータに直面した場合はビルダーを検討する 

 オプションパラメータが複数ある場合のよくある対処

  • javaBeanパターン
    • パラメータなしのコンストラクタを呼び出し、必須パラメータとオプションパラメータのsetterをそれぞれ呼び出して設定する
    • 欠点:生成過程の途中で不整合な状態となっている可能性 + クラスをimmutableに出来ない

 上記が望ましい解ではない場合、以下を考えてみる。

  • ビルダーパターン
    • オブジェクトを直接生成する代わりに必須パラメータを持つコンストラクタを呼び出してビルダーオブジェクトを作成する。そのビルダーオブジェクトにオプションパラメータを設定した後、immutableなクラスを作成するbuildメソッドを呼び出す。

 これに関しては例を見た方が手っ取り早い。

例)
public class Hoge {
  private final int requiredVal;
  private final int optionVal1;
  private final int optionVal2;
  
  public static class Builder {
     private final int requiredVal;
     private int optionVal1 = 0;  /* オプション */
     private int optionVal2 = 0;  /* オプション */
     
     public Builder(int requiredVal) { 
        requiredVal = requiredVal;
      }
      public Builder optionVal1(int optionVal1) { 
        optionVal1 = optionVal1; return this; 
      }
      public Builder optionVal2(int optionVal2) { 
        optionVal1 = optionVal2; return this; 
      }
      public Hoge build() {
        if(optionVal1 <= optionVal2) throw new IllegalStateException();
        return new Hoge(this);
      }
      
      private Hoge(Builder builder) {
        this.requiredVal= builder.requiredVal;
        this.optionVal1 = builder.optionVal1;
        this.optionVal2 = builder.optionVal2;
     }
}
Hoge hoge = new Hoge.Builder(100).optionVal1(20).optionVal2(10).build();

ポイントは以下の通りだ。

  • 対象クラスのコンストラクタはBuilderのみを引数で受け取るようにする
  • 対象クラスのインスタンス変数はfinalをつけてimmutableにする
  • Builderのオプション設定メソッドはBuilder自身を返すようにする(必須でないが読みやすくなる)
  • 対象クラスの不変式(インスタンス変数間の制約)が守られていない場合、buildメソッドはIllegalStateExceptionをスローすべき
  • オプション設定時に不変式をチェックする場合は、IllegalArgumentExceptionをスローする

 欠点といえば、この手のBeanを大量に必要とするO-Rマッパーが対応していないことぐらいだろうか。エンティティBeanなんてimmutableにできればどんなに楽かと思うが、たいてい対応しているのはjavaBeanパターンの方である。アノテーションとかで生成パターン指定できたら楽なのだけど。

参考文献

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)