クラス内に多くのフィールドがある場合、一般に次のような処理方法があります。
- 重複するコンストラクター
- 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();