「Effective Java」 2 番目の記事では、複数のコンストラクター パラメーターがある場合に Builder を使用してオブジェクトを作成します。

クラス内に多くのフィールドがある場合、一般に次のような処理方法があります。

  • 重複するコンストラクター
  • JavaBean パターン
  • ビルダーモード

1. オーバーラップコンストラクター

複数のコンストラクターを指定します。最初のコンストラクターには 1 つのオプションのパラメーターがあり、2 番目のコンストラクターには 2 つのパラメーターがあり、以下同様です。最後のコンストラクターには、フィールドの数と同じ数のオプションのパラメーターが含まれます。クライアント (このクラスを使用するクラス) は、コンストラクターを使用するときに、最も短いパラメーターを持つコンストラクターを探します。

1.1 コード例

public class NutritionFacts {
    
    
    /**
     * 每份含量
     */
    private final int servingSize;
    /**
     * 含量
     */
    private final int serving;
    /**
     * 卡路里
     */
    private final int calories;
    /**
     * 脂肪
     */
    private final int fat;
    /**
     * 钠
     */
    private final int sodium;
    /**
     * 碳水化合物
     */
    private final int carbohydrate;

    public NutritionFacts(int servingSize, int serving) {
    
    
        this(servingSize,serving,0);
    }

    public NutritionFacts(int servingSize, int serving, int calories) {
    
    
        this(servingSize,serving,calories,0);
    }

    public NutritionFacts(int servingSize, int serving, int calories, int fat) {
    
    
        this(servingSize,serving,calories,fat,0);
    }

    public NutritionFacts(int servingSize, int serving, int calories, int fat, int sodium) {
    
    
        this(servingSize, serving, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int serving, int calories, int fat, int sodium, int carbohydrate) {
    
    
        this.servingSize = servingSize;
        this.serving = serving;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = carbohydrate;
    }
}

クライアントはインスタンスを作成するときに、パラメーターに従ってコンストラクター呼び出しを直接選択します。

1.2 分析

  • 利点: 記述が簡単で、クラスの不変性 (つまり、作成後に変更できないこと) を保証できます。
  • 短所: (1) パラメータの選択は柔軟ではなく、最も短いものでも使用できないものがあります (2) クライアント コードが理解しにくい (3) クライアント コードはエラーが発生しやすく、2 つのパラメータの位置が間違って記述されています。等

2. JavaBeans パターン

このモードの実践方法は、空のパラメーター コンストラクターを使用してオブジェクトを作成し、set メソッドを使用してフィールドに値を割り当てることです。

2.1 コード

@Data
public class NutritionFacts02 {
    
    
    /**
     * 每份含量
     */
    private int servingSize;
    /**
     * 含量
     */
    private int serving;
    /**
     * 卡路里
     */
    private int calories;
    /**
     * 脂肪
     */
    private int fat;
    /**
     * 钠
     */
    private int sodium;
    /**
     * 碳水化合物
     */
    private int carbohydrate;

    public NutritionFacts02() {
    
    
    }
}

2.2 分析

  • 利点: クライアント コードは記述しやすく、理解しやすいです。

  • 欠点がある

    (1) 重複するコンストラクター NutritionFacts01 と比較すると、NutritionFacts02 のフィールドはすべて可変であるため、クラスを不変にすることはできませんが、オブジェクトの作成後も、set メソッドを通じてオブジェクトの状態を変更できます。

    (2) クラスの不変性が保証できない場合、状態不整合や同時実行性の問題が発生します。マルチスレッドでは、オブジェクトの構築プロセスがアトミックではないため、同時実行性の問題が発生します。

3. ビルダーモード

ビルダーパターンとは、ビルダーパターンの考え方をデザインパターンに利用することです。このモードでは、オブジェクトは直接生成されません。代わりに、最初に Builder オブジェクトを使用して、オブジェクトが作成されるフィールドの値を設定します。最後に、Builder オブジェクトの build メソッドを使用してオブジェクトが作成されます。最後に、build メソッドはターゲット クラスの Number コンストラクターを呼び出します。

これにより、JavaBeansのパターンと同様にオブジェクトフィールドの値を明示的に設定することができ、クラスの不変性が保証されます。

3.1 コード

public class NutritionFacts03 {
    
    

    private final int servingSize;
    private final int serving;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    private NutritionFacts03(Builder builder) {
    
    
        this.servingSize = builder.servingSize;
        this.serving = builder.serving;
        this.calories = builder.calories;
        this.fat = builder.fat;
        this.sodium = builder.sodium;
        this.carbohydrate = builder.carbohydrate;
    }

  	//必须是静态,要不然外部无法访问;不加访问修改符,默认是包访问路径,因此为了保证外部包可以访问,需要定义未public
    static public class Builder{
    
    
        private int servingSize;
        private int serving;
        private int calories;
        private int fat;
        private int sodium;
        private int carbohydrate;
      	
      	//显示设置对象域的值
        public Builder servingSize(int servingSize){
    
    
            this.servingSize = servingSize;
            return this;
        }

        public Builder calories(int calories){
    
    
            this.calories = calories;
            return this;
        }
        public Builder serving(int serving){
    
    
            this.serving = serving;
            return this;
        }
        public Builder fat(int fat){
    
    
            this.fat = fat;
            return this;
        }
        public Builder sodium(int sodium){
    
    
            this.sodium = sodium;
            return this;
        }
        public Builder carbohydrate(int carbohydrate){
    
    
            this.carbohydrate = carbohydrate;
            return this;
        }
      	//创建对象
        public NutritionFacts03 build(){
    
    
            return new NutritionFacts03(this);
        }
    }
}

クライアントの呼び出し:

NutritionFacts03 build = new Builder().servingSize(1).serving(2).build();

3.2 分析

  • アドバンテージ

    (1) クラスの不変性を確保する

    (2) 表示の作成、明確なクライアントコード

    (3) ドメイン値の妥当性チェックも非常に便利

  • 短所: Builder にもオーバーヘッドが発生し、パフォーマンスが低下します。

3.3 クラス階層の使用法

クラス階層の使用は、サブクラス オブジェクトの作成を指します。オブジェクトの親クラスは、複数のフィールドを含む抽象クラスです。サブクラス オブジェクトを構築するとき、サブクラスのビルダーは親クラスのフィールドを認識しません。 . このとき何が起こるでしょうか? 管理しますか?

(1) 親クラスのドメインをサブクラスのBuilderに追加できますが、継承レベルが多い場合、サブクラスのBuilderにも多くのドメインが存在します。

(2)親クラスにもBuilderを追加

コードは以下のように表示されます。

//父类
public abstract class Pizza {
    
    
    /**
     * 定义一个枚举类:打顶 火腿 蘑菇 洋葱 胡椒 香肠
     */
    public enum Topping {
    
    HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}

    /**
     * 一个成员变量
     */
    final Set<Topping> toppings;


    /**
     * 定义了一个带范型的都造器类,其实后面使用时范型参数就是Builder
     * @param <T>
     */
    abstract static class Builder<T>{
    
    
        /**
         * 构造器类中域和被构造的类的域一致
         */
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);

        /**
         * 构造器中为被构造的对象的域赋值,这里的域是一个集合,所以使用这种方式
         * @param topping
         * @return
         */
         public T addTopping(Topping topping){
    
    
             toppings.add(Objects.requireNonNull(topping));
             //返回构造器对象,可以链式变成
             return self();
         }

        /**
         *
         * @return 返回一个构造器对象
         */
         protected abstract T self();

        /**
         * 构建方法,在其中调用被构造的类的构造器,创建对象
         * @return
         */
         abstract Pizza build();
    }

    public Pizza(Builder<?> builder) {
    
    
        //使用构造器中的值给当前对象的域赋值,当前只有一个toppings
        this.toppings = builder.toppings.clone();
    }
}

//子类
public class NyPizza extends Pizza {
    
    
    /**
     * 定义一个枚举类型
     */
    public enum Size {
    
    SMALL, MEDIUM, LARGE}
    private Size size;

    /**
     *
     */
    public static class  Builder extends Pizza.Builder<Builder>{
    
    
        private final Size size;

        public Builder(Size size) {
    
    
            this.size = Objects.requireNonNull(size);
        }

        @Override
        Pizza build() {
    
    
            return new NyPizza(this);
        }

        @Override
        protected Builder self() {
    
    
            //可以做有效性检查
            return this;
        }
    }

    public NyPizza(Builder builder) {
    
    
        super(builder);
        size = builder.size;
    }
}

クライアントコール

Pizza p = new Builder(Size.SMALL).addTopping(Topping.SAUSAGE).build();

おすすめ

転載: blog.csdn.net/baidu_40120883/article/details/131852739