如果一个对象带有多个参数,程序员一般采用重叠构造器模式。但是该方法的缺点是,如果参数很长,客户端代码不仅很难编写,同时可读性也很差。
第二种替代方案:Java Beans。类中提供一个无参构造方法和对属性的mutator方法。该方法的缺点是容易线程安全问题。
第三种方案就是Builder模式。该方案既保证了重叠构造器模式的安全性,又保证像JavaBeans模式那样好的可读性。
考虑一个例子,一款食物产品通常附属有营养标识,有必须的,也有可选的。比如脂肪,钠,碳水化合物,加路里等。
下面用Builder模式来构造一个营养标识类(NutritionFacts):
// Builder Pattern
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;
// nested builder
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 carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(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;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
@Override
public String toString() {
return "NutrionFacts[servingSize:" + servingSize + ",servings:"
+ servings + ",fat:" + fat + ",calories:" + calories
+ ",sodium:" + sodium + ",carbohydrate:" + carbohydrate + "]";
}
public static void main(String args[]) {
NutritionFacts nf = new NutritionFacts.Builder(5, 10).calories(1)
.fat(2).carbohydrate(3).build(); // by default sodium = 0
System.out.println(nf);
}
}
该模式在Android SDK中也有应用。比如我们最熟悉的警告框-AlertDialog,该类就嵌套了一个共有的静态Builder类。如下:
public static class Builder {
private final AlertController.AlertParams P;
private int mTheme;
/**
* Constructor using a context for this builder and the {@link AlertDialog} it creates.
*/
public Builder(Context context) {
this(context, com.android.internal.R.style.Theme_Dialog_Alert);
}
/**
* Constructor using a context and theme for this builder and
* the {@link AlertDialog} it creates.
*/
public Builder(Context context, int theme) {
P = new AlertController.AlertParams(context);
mTheme = theme;
}
/**
* Set the title using the given resource id.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setTitle(int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
/**
* Set the title displayed in the {@link Dialog}.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
/**
* Set the title using the custom view {@code customTitleView}. The
* methods {@link #setTitle(int)} and {@link #setIcon(int)} should be
* sufficient for most titles, but this is provided if the title needs
* more customization. Using this will replace the title and icon set
* via the other methods.
*
* @param customTitleView The custom view to use as the title.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setCustomTitle(View customTitleView) {
P.mCustomTitleView = customTitleView;
return this;
}
/**
* Set the message to display using the given resource id.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setMessage(int messageId) {
P.mMessage = P.mContext.getText(messageId);
return this;
}
/**
* Set the message to display.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
/**
* Set the resource id of the {@link Drawable} to be used in the title.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setIcon(int iconId) {
P.mIconId = iconId;
return this;
}
/**