The HIRO Says

If you smell what The HIRO is cooking!!!

builder pattern

【テーマ】

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

【概要】

GoFデザインパターンの1つ、builderパターンを使用する方法。
パラメータの設定やimmutableなオブジェクトの生成は、Builderクラスに任せます。

【補足】

インスタンスを生成する場合は、(1)まずBuilderのインスタンスを生成し、(2)その後必要に応じてbuilderのメソッドを呼び出して、(3)最後にbuildメソッドを呼び出します。

NutritionFacts target =
        new NutritionFacts.Builder(12, 34)
                .calories(5)
                .fat(6)
                .sodium(7)
                .carbohydrate(8)
                .build();

【コード例】

/**
 * 栄養成分表を表すエンティティクラス
 */
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
    // Builder を介して値を設定する。
    private NutritionFacts(Builder builder) {
        this.servingSize  = builder.servingSize;
        this.servings     = builder.servings;
        this.calories     = builder.calories;
        this.fat          = builder.fat;
        this.sodium       = builder.sodium;
        this.carbohydrate = builder.carbohydrate;
    }


// getter は省略



// Builder
    /**
     * builder クラス
     */
    public static class Builder {

        // fields
        private final int servingSize;
        private final int servings;
        private int calories;
        private int fat;
        private int sodium;
        private int carbohydrate;

        // constructors
        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

        // methods
        /**
         * カロリーを設定する。
         * 
         * @param calories 設定するカロリー
         * @return Builder インスタンス
         */
        public Builder calories(int calories) {
            this.calories = calories;
            return this;
        }

        /**
         * 脂肪を設定する。
         * 
         * @param fat 設定する脂肪
         * @return Builder インスタンス
         */
        public Builder fat(int fat) {
            this.fat = fat;
            return this;
        }

        /**
         * ナトリウムを設定する。
         * 
         * @param sodium 設定するナトリウム
         * @return Builder インスタンス
         */
        public Builder sodium(int sodium) {
            this.sodium = sodium;
            return this;
        }

        /**
         * 炭水化物を設定する。
         * 
         * @param carbohydrate 設定する炭水化物
         * @return Builder インスタンス
         */
        public Builder carbohydrate(int carbohydrate) {
            this.carbohydrate = carbohydrate;
            return this;
        }

        /**
         * Builder インスタンスを介して、NutritionFacts のインスタンスを生成する。
         * 
         * @return NutritionFacts のインスタンス
         */
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }
}

【長所】

  1. オブジェクトの値の不変性が、仕組みとして確保できます。(telescoping constructor pattern の利点の確保)
  2. コンストラクタでのうっかりミスで項目の順番を間違えるということがなくなります。(JavaBeans pattern の利点の確保)

【短所】

  1. Builderを介在させるため、パフォーマンス(メモリの使用量や実行時間)で他のパターンよりも劣るかもしれません。