当一个类中的域非常多的时候一般有以下几种处理方式:
- 重叠构造器
- JavaBean模式
- Builder模式
1、重叠构造器
提供多个构造器,第一个构造器有1个可选参数、第二个有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、JavaBeans模式
该模式的做法是使用空参构造器穿件对象,然后使用set方法给域赋值。
2.1 code
@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模式是使用设计模式中建造者模式的思想。该模式中,不直接生成对象,而是先利用一个Builder对象设置要创建对象的域的值,最后使用Builder对象的build方法创建对象,最后build方法调用目标类的数构造器。
这样既可以像JavaBeans的模式一样显示的设置对象域的值,又可以保证类的不可变。
3.1 code
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 类层次结构使用
类层次结构的使用是指一个子类对象的创建,该对象的父类是抽象类,其中包含若干域,此时构造子类对象时,子类的Builder不知道父类的域,这时候怎么办?
(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();