一: 其实前几天就已经看过了这个模式,当时没什么感觉,纸上谈兵,今晚去图书馆看《Effective Java》,突然有了一点新的领悟,写下此文。
参考书《设计模式-java》结城浩 著,博硕文化 译
《Effective Java》中文版,美Joshua Bloch著,杨春花 俞黎敏 译。强烈推荐,虽然才选看了一两节,绝对好书。
二:什么时候使用Builder模式
顾名思义,Builder模式主要用来是产生对象的,也就是说和静态工厂和构造器是并列级别的,那什么时候用这个Builder模式,那就是当有大量可选参数来构建对象的时候,这时候前两者都不太适合做,因为可选参数的可能太多,不适合一一枚举出来,,而且不适合调用,容易出错。
遇到构造器参数很多的时候,还有第二种替代方法,即JavaBeans模式,在这种模式下,调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个可选的参数。这样比较容易使用,创建实例很容易,可选参数设置也很easy,遗憾的是(下面的内容我现在只能理解,但是并没有实际中遇到过),javabeans模式自身带有很严重的缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能出于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败。另一点不足是,JavaBeans模式阻止了把类做成不可变的可能,这就需要程序员付出额外的努力来确保它的线程安全。 用现在流行的话来形容:不明觉历!
下面就是Builder模式的一种变形上场了,当然它必须解决上述的问题 。不直接生成想要的对象,而是要让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个Builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后客户端调用无参的build方法来生成不可变的对象。这个builder是它构建的类的静态成员类。
下面是代码:
public class NutritionFacts {
private final int servingSize;//这里只是列举比较少的参数
private final int servings;
private final int calaries;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder{
//required parameters
private final int servingSize;
private final int servings;
//optional parameters--initialized to default values
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize,int servings){
this.servingSize = servingSize;
this.servings = servings;
}
public Builder caloies(int val){
calories = val;
return this;
}
public Builder fat(int val){
fat = val;
return this;
}
public Builder carbohydrate(int val){
carbohydrate = val;
return this;
}
public Builder sodium(int val){
sodium = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder){
servingSize = builder.servingSize;
servings = builder.servings;
calaries = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
注意NutritionFacts是不可变,所有默认的参数值都单独放在一个地方。builder的setter方法返回本身,以方便把调用连接起来。感觉整个实现,利用静态类部类,实现的很巧妙。下面就是调用代码:
NutritionFacts cocacloa = new NutritionFacts.Builder(240, 8).caloies(100).sodium(35).carbohydrate(22).build();
这样的客户端代码很容易写,builder模式模拟了具名的可选参数。
好了,写到这,我有点迷惑了,因为网上的关于Builder模式描述无外乎:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。实际的实现过程中,感觉就是我上次写的模版设计模式,只是偏重于产生对象。前者偏重于通过聚合来组装对象,后者偏重于通过继承来重写对象的行为。
下面是GOF对Builder模式的部分阐述,先列出来。文字很精辟。
(1) 意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
(2) 适用性:
当同时满足以下情况的时候可以使用Builder模式
a. 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式;
b. 当构造过程必须允许构造的对象有不同的表示;
只能说,没有大量的实践经验,学习设计模式往往并不能深入的理解吧,先就这么写着吧,等以后忽然哪天领悟了,再来更正。