遇到多个构造器参数时要考虑使用构建器
引入:当我们创建对象传递参数的时候,往往通过构造方法来传,如下代码:
public class Person {
private String name;
private String id;
private int age;
public Person(String name) {
this.name = name;
}
public Person(String id, String name) {
this.id = id;
this.name = name;
}
public Person(String name, String id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
}
测试类:
public class TestBuilder {
public static void main(String[] args) {
Person person=new Person("tom");
Person person1=new Person("joe","id123");
Person person2=new Person("lin","id123",24);
}
}
上面的代码可以使用new构造,也可以用set方法来传入参数。但是不管怎么样 ,如果参数不断的增加,而且增加可选,必选字段,这样使我们的代码很难编写,而且不容易实现。
1、Builder定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2、使用场景
1、相同的方法,不同的执行顺序,产生不同的事件结果
2、对象比较复杂,参数比较多。
3、uml类图
4、示例代码:
食品标签中
package bean;
/**
* 食品营养标签
*/
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;//碳水化合物
@Override
public String toString() {
return "NutritionFacts{" +
"servingSize=" + servingSize +
", servings=" + servings +
", calories=" + calories +
", fat=" + fat +
", sodium=" + sodium +
", carbohydrate=" + carbohydrate +
'}';
}
public static class Builder {
//必选字段
private int servingSize;
private int servings;
//可选字段,添加默认属性
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
//通过builder模式,给对象添加属性;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int calories) {
this.calories = calories;
return this;
}
public Builder fat(int fat) {
this.fat = fat;
return this;
}
public Builder carbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
return this;
}
public Builder sodium(int sodium) {
this.sodium = sodium;
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;
}
}
测试代码:
NutritionFacts nutritionFacts = new NutritionFacts.Builder(240, 8)
.calories(100)
.sodium(35)
.carbohydrate(27)
.build();
运行结果:
NutritionFacts: NutritionFacts{servingSize=240, servings=8, calories=100, fat=0, sodium=35, carbohydrate=27}
5、Android源码中的Builder模式
Android中Builder模式也是常见的,最熟悉的是Dialog
AlertDialog.Builder dialog= new AlertDialog.Builder(this);
dialog.setTitle("title")
.setMessage("message")
.create()
.show();
源码分析:
public class AlertDialog extends AppCompatDialog implements DialogInterface {
final AlertController mAlert;
public static class Builder {
private final AlertController.AlertParams P;
private final int mTheme;
public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
mAlert.setButton(whichButton, text, listener, null);
}
public void setIcon(int resId) {
mAlert.setIcon(resId);
}
......
public Builder(@NonNull Context context) {
this(context, resolveDialogTheme(context, 0));
}
public Builder(@NonNull Context context, @StyleRes int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
mTheme = themeResId;
}
@NonNull
public Context getContext() {
return P.mContext;
}
public Builder setTitle(@StringRes int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
public Builder setTitle(@Nullable CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setCustomTitle(@Nullable View customTitleView) {
P.mCustomTitleView = customTitleView;
return this;
}
public Builder setMessage(@StringRes int messageId) {
P.mMessage = P.mContext.getText(messageId);
return this;
}
......
//返回AlertDialog对象
public AlertDialog create() {
// We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
// so we always have to re-set the theme
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
6、总结
与构造器相比,builder的微略优势在于,builder可以有多个可变参数,构造器就像方法一样,只能有一个可变参数。因为builder利用单独
的方法来设置每个参数,你想要多少个可变参数,它们就可以有多少个,直到每个setter方法都有一个可变参数。
Builder模式十分灵活,可以利用单个builder构建多个对象,builder的参数可以在创建对象期间进行调整,也可以随着不同的对象而改变。builder
可以自动填充其些域,例如每次创建对象时自动增加。
简而言之,如果类的构建器或者静态工厂有多个参数,设计这种时,Builder模式就是不错的选择,特别是当大多数参数都是可选的时候。与
使用传统的重叠构造器模式相比,使用builder模式的客户端代码装将更易于阅读和编写,构建器也比JavaBeans更加安全。
ref 《effective java》《Android源码设计模式解析与实战》