The HIRO Says

If you smell what The HIRO is cooking!!!

Telescoping constructor pattern

Effective Javaの全項目を網羅してまとめようとしたら内容が多すぎたのでシフトチェンジ。
同著を読んで気がついたテクニックを、少しずつうpしていきます。
完全に自己満足ですええ。(だって英語版難しいんですもの)
補足があったらよろしくです。

【テーマ】

コンストラクタの引数が多い場合、何か良い方法はないか?

【概要】

「telescope」という単語には、「望遠鏡」という意味もありますが、ここでは「順番にあてはめる」という意味です。
「1つ1つ順番にあてはめられる」ように、受け取れる引数が1つずつ増えていくようにコンストラクタを作成し、それらを順番に呼び出そうというのがこの方法の特徴です。

【補足】

  1. 各項目は勝手に変えられないよう、finalで定義しています。(「不変オブジェクト」とか言うらしいです。)
  2. インスタンス変数をfinalで定義すると、(1)定義時かコンストラクタで必ず値を設定しないとダメ!(2)setterは禁止!となります。(コンパイルエラーになります)
NutritionFacts target = new NutritionFacts(350, 350, 48, 0, 5, 18);

【コード例】

/**
 * 栄養成分表を表すエンティティクラス
 */
public class NutritionFacts {

// fields
    // 内容量(必須)
    private final int servingSize;
    // 1人前の量(必須)
    private final int servings;
    // カロリー(任意)
    private final int calories;
    // 脂肪(任意)
    private final int fat;
    // ナトリウム(任意)
    private final int sodium;
    // 炭水化物(任意)
    private final int carbohydrate;


// constructor
    /**
     * 必須項目のみを設定できるコンストラクタ
     */
    public NutritionFacts(
            int servingSize,
            int servings) {

        this(servingSize,
                servings,
                0);
    }


    /**
     * 必須項目+カロリーを設定できるコンストラクタ
     */
    public NutritionFacts(
            int servingSize,
            int servings,
            int calories) {

        this(servingSize,
                servings,
                calories,
                0);
    }


    public NutritionFacts(
            int servingSize,
            int servings,
            int calories,
            int fat) {

        this(servingSize,
                servings,
                calories,
                fat,
                0);
    }


    public NutritionFacts(
            int servingSize,
            int servings,
            int calories,
            int fat,
            int sodium) {

        this(servingSize,
                servings,
                calories,
                fat,
                sodium,
                0);
    }


    /**
     * 全項目をパラメータとして設定できるコンストラクタ。
     */
    public NutritionFacts(
            int servingSize,
            int servings,
            int calories,
            int fat,
            int sodium,
            int carbohydrate) {

        this.servingSize  = servingSize;
        this.servings     = servings;
        this.calories     = calories;
        this.fat          = fat;
        this.sodium       = sodium;
        this.carbohydrate = carbohydrate;
    }


// getter は省略
}

【長所】

オブジェクトの値の不変性を、仕組みとして確保できます。

【短所】

実際にコンストラクタを呼び出す際、うっかりミスで項目の順番を間違えやすいですね。
また、もし順番を間違えていた場合、しっかりした単体テストを用意しないとエラーの検出が難しいですね。

【関連情報】

  1. JavaBeans pattern
  2. builder pattern