Builder建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们,用户不需要了解所构建对象的内部具体构建细节,Builder建造设计模式的目的是为了将构造复杂对象的过程和它的部件解耦。
Builder建造者设计模式中有两个重要角色:Director指导者和Builder建造者。Director指导者相当于设计师或架构师,拥有整个产品各个部件之间关系的构建蓝图。Builder建造者是部件的具体创建者,Builder建造者根据Director指导者的指示创建产品的各个部件,最终由Director构建出完整产品。Builder建造者设计模式的UML图如下:
《Effective java》中有一个关于建造者模式的例子如下:
用一个类表示包装食品外面显示的营养成分,其中每份含量和每罐含量是必须的,卡洛里,脂肪,纳和碳水化合物是可选参数。
大家一般习惯使用重载构造方法来解决该问题,代码如下:
public class NutritionFacts{
//必须参数
private final int servingSize;
private final int servings;
//可选参数
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
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;
}
}
如果想要创建实例对象的时候,就利用参数列表最短的构造方法:
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
public class NutritionFacts{
//必须参数
private int servingSize = -1;
private int servings = -1;
//可选参数
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts(){}
public void setServingSize(int val){
this.servingSize = val;
}
public void setServings(int val){
this.servings = val;
}
public void setCalories(int val){
this.calories = val;
}
public void setFat(int val){
this.fat = val;
}
public void setSodium(int val){
this.sodium = val;
}
public void setCarbohydrate(int val){
this.carbohydrate = val;
}
}
使用setter方法可以弥补重载构造方法的缺陷,创建实例对象很容易,并且代码也容易阅读:
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setFat(27);
由于javaBean自身有着很严重的缺点,构造过程被分到了几个调用中,在构造过程中java Bean可能处于不一致的状态,类无法仅仅通过检验构造方法参数的有效性来保证一致性,使用处于不一致状态的对象将会导致失败。另外javaBean阻止了把类做成不可变的可能,因此很难确保线程安全。
public class NutritionFacts{
//必须参数
private final int servingSize;
private final int servings;
//可选参数
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
//建造者
public static class Builder{
//必须参数
private final int servingSize;
private final int servings;
//可选参数
private final int calories = 0;
private final int fat = 0;
private final int sodium = 0;
private final int carbohydrate = 0;
public Builder(int servingSize, int servings){
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val){
this.calories = val;
return this;
}
public Builder fat(int val){
this.fat = val;
return this;
}
public Builder sodium(int val){
this.sodium = val;
return this;
}
public Builder carbohydrate(int val){
this.carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
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;
}
}
使用建造者模式创建实例对象:
NutritionFacts cocaCola = new NutritionFacts().Build(240, 8).calories(100).sodium(35)
.carbohydrate(27).build();
建造者模式可以有多个可变参数,可以利用建造器构建多个对象,参数可以在创建时动态调整。
Builder建造者模式和AbstraceFactory抽象工厂模式的区别:
Builder建造者模式和AbstraceFactory抽象工厂模式非常类似,很多人经常分不清楚,区别如下:
(1).抽象工厂模式中,每一次工厂物件被呼叫时都会传回一个完整的产品物件,而使用端有可能会决定把这些产品组装成一个更大的和复杂的产品,也有可能不会。工厂物件是没有状态的,不知道上一次构建的是哪一个产品,也没有未来的概念,不知道下一次构建的是哪一个产品,更不知道自己构建的产品在更高层的产品结构蓝图中是什么位置。 换言之,虽然抽象工厂模式与建造模式都是设计模式,但是抽象工厂模式处在更加具体的尺度上,而建造模式则处于更加宏观的尺度上。一个系统可以由一个建造模式和一个抽象工厂模式组成,使用端通过呼叫这个导演角色,间接地呼叫另一个抽象工厂模式的工厂角色。工厂样式传回不同产品族的零件,而建造者模式则把它们组装起来。
JDK中建造者模式的应用:
StringBuilder和StringBuffer的append()方法使用了建造者模式。