1.builder模式(建造者模式)
建造者模式是一种创建型设计模式。它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

使用场景:
①相同方法,不同的执行顺序,产生不同的事件结果时;
②多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
③产品类非常复杂,或者产品类中的调用顺序不同产生了不同效能,这个时候使用建造者模式非常合适。

优点:使用灵活,易于扩展;不用关心内部实现细节,只注重结果。
缺点:产生多余的Builder对象,消耗内存。

2.builder模式用法
假如有一个类Student,它有很多的属性,但是仅仅姓名和学号是必须赋值的,其他的属性都是可选项:

public class Student {
 private final int stuId;//必须
 private final String name;//必须
 private final int age;//可选
 private final int gender;//可选
 private final int address;//可选
 …//还有很多可选属性
 }


那怎么来创建一个Student对象呢?由于每个属性都用final修饰了,说明每个属性都要在构造方法中被初始化,必须提供各种参数数量的构造方法,如果不使用建造者模式,如下:

public class Student {
 private final int stuId;//必须
 private final String name;//必须
 private final int age;//可选
 private final int gender;//可选
 private final String address;//可选public Student(int stuId,String name){
 this(stuId,name,0,1,"");
 }
 public Student(int stuId,String name,int age){
 this(stuId,name,age,1,"");
 }
 public Student(int stuId,String name,int age,int gender){
 this(stuId,name,age,gender,"");
 }
 public Student(int stuId,String name,int age,int gender,String address){
 this.stuId = stuId;
 this.name = name;
 this.age = age;
 this.gender = gender;
 this.address = address;
 }
 }


这样做确实可以满足需求,但这还是可选参数不多的情况,如果有很多可选参数,就必须要写很多个构造函数,这将导致代码的可读性和维护性变差,更重要的是当用到这个类的时候会感觉无从下手,到底应该用哪个构造方法呢?应该用两个参数的构造方法还是用三个参数的呢?如果用两个参数的构造方法,那么可选参数的默认值是多少?
更棘手的是,如果只想给Student对象设置address属性而不设置age和gender属性的话怎么办?显然还得再继续添加构造方法,或者只能调用全参的构造方法,然后给age和gender属性设置个默认值。
还有一点,stuId,age,gender都是int类型的,那在创建Student对象时,哪一个int类型的对象代表stuId,哪一个代表age,这还进一步增加了使用成本。
为了解决这些问题,可以修改代码,只设置一个默认的无参构造方法,然后给每个属性添加getter和setter方法:

public class Student {
 private int stuId;//必须
 private String name;//必须
 private int age;//可选
 private int gender;//可选
 private String address;//可选
 public Student(){
 }
 public int getStuId() {
 return stuId;
 }
 public void setStuId(int stuId) {
 this.stuId = stuId;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public int getAge() {
 return age;
 }
 public void setAge(int age) {
 this.age = age;
 }
 public int getGender() {
 return gender;
 }
 public void setGender(int gender) {
 this.gender = gender;
 }
 public String getAddress() {
 return address;
 }
 public void setAddress(String address) {
 this.address = address;
 }
 }


这种方法看上去可读性和维护性比较好,当使用这个类的时候只需要创建一个空的对象,并且设置我们需要的属性就可以了。
比如这样:

Student stu = new Student();
 stu.setStuId(1);
 stu.setName(“小明”);
 stu.setAge(12);


这样做有两个问题,第一个问题是stu对象没有一个创建完毕的标识,上面的stu对象设置了三个属性,但当别人看到这段代码时,他不确定这个stu对象是只需要这三个属性还是当时作者忘了写完整,除非所有的属性都给set上,别人才能确保你这个对象创建完毕;另一个问题是任何人都可以在我们创建好的基础上继续改变它,也就是继续给它set新的属性或者删除某个已经set的属性,这就会使stu对象具有可变性,引起潜在的风险。

为了解决以上两种方法的所有问题,就需要用到builder设计模式了。

builder模式使用方法如下:

public class Student {
 private final int stuId;//必须
 private final String name;//必须
 private final int age;//可选
 private final int gender;//可选
 private final String address;//可选private Student(StudentBuilder builder){
 this.stuId = builder.stuId;
 this.name = builder.name;
 this.age = builder.age;
 this.gender = builder.gender;
 this.address = builder.address;
 }public int getStuId() {
 return stuId;
 }
 public String getName() {
 return name;
 }
 public int getAge() {
 return age;
 }
 public int getGender() {
 return gender;
 }
 public String getAddress() {
 return address;
 }
 public static class StudentBuilder{
 private final int stuId;
 private final String name;
 private int age;
 private int gender;
 private String address;public StudentBuilder(int stuId,String name){
 this.stuId = stuId;
 this.name = name;
 }
 public StudentBuilder setAge(int age){
 this.age = age;
 return this;
 }
 public StudentBuilder setGender(int gender){
 this.gender = gender;
 return this;
 }
 public StudentBuilder setAddress(String address){
 this.address = address;
 return this;
 }
 public Student build(){
 return new Student(this);
 }
 }
 }


注意:
①Student的构造方法是私有的,也就是说不能直接new出Student对象;
②将Student的属性用final修饰了,并且在构造方法中都为它们进行了初始化操作,只提供了getter方法;
③使用builder模式构造出来的对象有更好的可读性;
④StudentBuilder的属性中只给必须的属性添加了final修饰,所以必须在StudentBuilder的构造方法中初始化它们。

使用builder设计模式完美的解决了方法一和方法二的不足,并且兼具他们的优点:具有必填属性和可选属性的区分,更重要的是:可读性很强。唯一的不足是要在StudentBuilder中重复的写一遍Student中的属性。

接下来创建一个Student对象:

public Student getStudent(){
 return new Student.StudentBuilder(1,“小明”) //必填属性在构造方法中赋值
 .setAge(1) //设置可选属性
 .setGender(1) //设置可选属性
 .build(); //对象构建完毕,返回Student对象
 }


代码非常优雅,这是一个链式调用,用1行代码就搞定,更重要的是可读性非常强,而且通过build()可以很明确的告诉别人Student已经创建完毕。

builder设计模式非常灵活,一个builder可以创建出各种各样的对象,只需要在build()之前调用set方法来为对象赋值。
builder模式另一个重要特性是:它可以对参数进行合法性验证,如果传入的参数无效,可以抛出一个IllegalStateException异常,但是在哪里进行参数合法性验证也是有讲究的:那就是在对象创建之后进行合法性验证。修改StudentBuilder的build()方法:

public Student build(){
 Student student = new Student(this);
 if (student.getAge()>120){
 throw new IllegalStateException(“年龄超出限制”);
 }
 return student;
 }


为什么要先创建对象,再进行参数验证?因为StudentBuilder是线程不安全的,如果先进行参数验证后创建对象,那么创建对象的时候对象的属性可能已经被其他线程改变了。例如下面的代码就是错误的:

public Student build(){
 if (age>120){
 throw new IllegalStateException(“年龄超出限制”);
 }
 return new Student(this);
 }

最后总结一下builder设计模式:当类中有很多属性的时候,尤其是有很多可选属性的时候,就可以使用builder设计模式,因为这样不仅可以使类使用起来很优雅,而且还可以给类的对象一个创建完成的标识,即build()方法。

3.Android源码中的Builder模式
Android中Builder模式很常见,比如Dialog。

AlertDialog.Builder dialog= new AlertDialog.Builder(this);
 dialog.setTitle(“title”)
 .setMessage(“message”)
 .create()
 .show();

Dialog源码分析:

public class AlertDialog extends Dialog implements DialogInterface {
 private AlertController mAlert; //Controller, 接受Builder成员变量P中的各个参数
 protected AlertDialog(Context context, int theme) {
 this(context, theme, true);
 }
 AlertDialog(Context context, int theme, boolean createContextWrapper) {
 super(context, resolveDialogTheme(context, theme), createContextWrapper);
 mWindow.alwaysReadCloseOnTouchAtt r();
 mAlert = new AlertController( getContext(), this, getWindow());
 }
 @Override
 public void setTitle(CharSequence title) {
 super.setTitle(title);
 mAlert.setTitle(title); //实际上调用的是mAlert的setTitle方法
 }
 public void setCustomTitle(View customTitleView) {
 mAlert.setCustomTitle(customTitleView); //实际上调用的是mAlert的setCustomTitle方法
 }
 public void setMessage(CharSequence message) {
 mAlert.setMessage(message);
 }
 ……
 // Builder为AlertDialog的内部类
 public static class Builder {
 private final AlertController.AlertParams P; //存储AlertDialog的各个参数
 public Builder(Context context) {
 this(context, resolveDialogTheme(context, 0));
 }
 public Builder(Context context, int theme) {
 P = new AlertController.AlertParams( new ContextThemeWrapper(context, resolveDialogTheme(context, theme)));
 mTheme = theme;
 }
 …
 public Builder setTitle(CharSequence title) {
 P.mTitle = title;
 return this;
 }
 public Builder setMessage(CharSequence message) {
 P.mMessage = message;
 return this;
 }
 public Builder setIcon(int iconId) {
 P.mIconId = iconId;
 return this;
 }
 public Builder setPositiveButton( CharSequence text, final OnClickListener listener) {
 P.mPositiveButtonText = text;
 P.mPositiveButtonListener = listener;
 return this;
 }
 public Builder setView(View view) {
 P.mView = view;
 P.mViewSpacingSpecified = false;
 return this;
 }
 public AlertDialog create() {
 final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); //构造AlertDialog对象, 并将参数传给AlertDialog
 P.apply(dialog.mAlert); //将P中的参数应用在dialog中的mAlert对象中
 dialog.setCancelable(P.mCancelable);
 if (P.mCancelable) {
 dialog.setCanceledOnTouchOutside( true);
 }
 dialog.setOnCancelListener( P.mOnCancelListener);
 if (P.mOnKeyListener != null) {
 dialog.setOnKeyListener( P.mOnKeyListener);
 }
 return dialog;
 }
 }
 }


通过Builder来设置AlertDialog中的title,、message、button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。
看看apply函数的实现 :

public void apply(AlertController dialog) {
 if (mCustomTitleView != null) {
 dialog.setCustomTitle( mCustomTitleView);
 } else {
 if (mTitle != null) {
 dialog.setTitle(mTitle);
 }
 if (mIcon != null) {
 dialog.setIcon(mIcon);
 }
 if (mIconId >= 0) {
 dialog.setIcon(mIconId);
 }
 if (mIconAttrId > 0) {
 dialog.setIcon( dialog.getIconAttributeResId(mIconAttrId));
 }
 }
 if (mMessage != null) {
 dialog.setMessage(mMessage);
 }
 if (mPositiveButtonText != null) {
 dialog.setButton( DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,mPositiveButtonListener, null);
 }
 if (mNegativeButtonText != null) {
 dialog.setButton( DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,mNegativeButtonListener, null);
 }
 if (mNeutralButtonText != null) {
 dialog.setButton( DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,mNeutralButtonListener, null);
 }
 if (mForceInverseBackground) {
 dialog.setInverseBackgroundForced(true);
 }
 if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
 createListView(dialog);
 }
 if (mView != null) {
 if (mViewSpacingSpecified) {
 dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
 } else {
 dialog.setView(mView);
 }
 }
 }


就是把P中的参数依次设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中可以看到,实际上也都是调用了mAlert对应的setter方法。

4.总结
与构造器相比,builder的优势在于,builder可以有多个可变参数,构造器就像方法一样,只能有一个可变参数。因为builder利用单独
的方法来设置每个参数,想要多少个可变参数,它们就可以有多少个,直到每个setter方法都有一个可变参数。
Builder模式十分灵活,可以利用单个builder构建多个对象,builder的参数可以在创建对象期间进行调整,也可以随着不同的对象而改变。builder
可以自动填充其些域,例如每次创建对象时自动增加。
总之,如果类的构建器或者静态工厂有多个参数,Builder模式是不错的选择,特别是当大多数参数都是可选的时候。