Builder模式既构建者模式,可以一步一步地创建一个复杂的对象。记住是复杂的对象,简单的在使用构建者就冗余了。很多的开源项目中也都用到了Builder模式,比如Rtrofit , Glide ,Picasso RxJava 等等,安卓系统中用到的也很多,最典型的就是我的的AertDialog。他们的很大的一个特点就是链式调用。使我们的代码写起来既简单又爽快。而链式调用的关键就是每个setter方法都返回自身。

下面举个简单的例子
有一个对象Person

public class Person {
    private String name;
    private String sex;
    private double height;
    private String address;
    private String phone;
    private String email;

    public Person(String name, String sex, double height, String address, String phone, String email) {
        this.name = name;
        this.sex = sex;
        this.height = height;
        this.address = address;
        this.phone = phone;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

正常我们创建这个Person对象是

Person person = new Person("大海","男",175,"北京","18813137878","136@163.com");

上面的方法可以构建出Person对象,但是缺点也很明显,当Person中的参数有很多的时候,我们在构建的时候,根本不知道下面的参数是哪个,还得去类里面看一下构造参数,在返回来填写。
另外当我们的Person对象有各种各样的构造实现,每种构造需要的参数都不一样,那我们的实体类中就需要写各种的参数的构造方法,当我们构造对象的时候更加不容易知道我们需要填写的参数是神马。
当然我们也可以先构造没有参数的对象。在一个一个set参数,这样也很麻烦哦,写一堆person.set()……

然后看一下builder模式的构建方式

public class Person {
    private String name;
    private String sex;
    private double height;
    private String address;
    private String phone;
    private String email;

   public static class Builder{
       Person mPerson;
       public Builder() {
        mPerson = new Person();
       }
       public  Builder name(String name){
           mPerson.name = name;
           return this;
       }
       public  Builder sex(String sex){
           mPerson.sex = sex;
           return this;
       }
       public  Builder height(double height){
           mPerson.height = height;
           return this;
       }
       public  Builder address(String address){
           mPerson.address = address;
           return this;
       }
       public  Builder phone(String phone){
           mPerson.phone = phone;
           return this;
       }
       public  Builder email(String email){
           mPerson.email = email;
           return this;
       }
       public Person build(){
           return mPerson;
       }
   }
}

构建上面的Person类我们就可以这样:

Person person = new Person.Builder()
            .name("dahai")
            .sex("man")
            .address("beijing")
            .height(175)
            .phone("18813137878")
            .email("136@163.com")
            .build();

这样看起来非常清晰,而且我们可以想要几个参数就添加几个参数很方便。比直接set参数简单很多,比使用构造器灵活很多。所以说builder模式是用来一步一步构建复杂的对象的。

下面来了解下AertDialog构建者模式

正常开发中使用AertDialog 一般都是如下;

new AlertDialog.Builder(this)
                .setIcon(ContextCompat.getDrawable(this,R.mipmap.ic_launcher))
                .setTitle("弹窗标题")
                .setMessage("弹窗内容")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                })
                .create()
                .show();

首先从类名(AlertDialog.Builder)就可以看出这是一个Builder模式。通过Builder对象来构建dialog的各个部分,我们可以根据自己的需要来添加部件,如上面的setTitle , setMessage等。
AlertDialog中的部分代码:

public class AlertDialog extends AppCompatDialog implements DialogInterface {

    final AlertController mAlert;

    static final int LAYOUT_HINT_NONE = 0;
    static final int LAYOUT_HINT_SIDE = 1;

    protected AlertDialog(@NonNull Context context) {
        this(context, 0);
    }


    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, resolveDialogTheme(context, themeResId));
        mAlert = new AlertController(getContext(), this, getWindow());
    }

    protected AlertDialog(@NonNull Context context, boolean cancelable,
            @Nullable OnCancelListener cancelListener) {
        this(context, 0);
        setCancelable(cancelable);
        setOnCancelListener(cancelListener);
    }

    public Button getButton(int whichButton) {
        return mAlert.getButton(whichButton);
    }


    public ListView getListView() {
        return mAlert.getListView();
    }

    @Override
    public void setTitle(CharSequence title) {
        super.setTitle(title);
        mAlert.setTitle(title);
    }


    public void setCustomTitle(View customTitleView) {
        mAlert.setCustomTitle(customTitleView);
    }


    public void setMessage(CharSequence message) {
        mAlert.setMessage(message);
    }


    public void setView(View view) {
        mAlert.setView(view);
    }  
...... 省略各种set方法......

可以看到 AlertDialog中的set方法设置的参数 最终都保存在成员变量AlertController中。

Builder 中的代码

public static class Builder {
        private final AlertController.AlertParams P;
        private final int mTheme;


        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(CharSequence title) {
            P.mTitle = title;
            return this;
        }


        public Builder setCustomTitle(View customTitleView) {
            P.mCustomTitleView = customTitleView;
            return this;
        }


        public Builder setMessage(@StringRes int messageId) {
            P.mMessage = P.mContext.getText(messageId);
            return this;
        }


        public Builder setMessage(CharSequence message) {
            P.mMessage = message;
            return this;
        }


...... 中间省略一堆set ......

        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;
        }
    }

可以看到 Builder中有一个成员变量AlertController.AlertParams。我的所set的值都保存AlertController.AlertParams中。AlertController.AlertParams包含了与AlertDialog 视图中对应的成员变量,在调用Builder中的create()的时候,会创建AlertDialog 并通过P.apply(dialog.mAlert)将AlertController.AlertParams中的参数保存在AlertDialog的成员变量AlertController中,然后返回dialog。

标准的builder模式还有一层Director类来封装Builder,但是一般真实的开发中一边都省略Director,直接链式的调用会更加清晰简单。

平时的工作中,我们自己写一些工具类的时候,都可以使用builder模式,来使我们的工具类更加方便使用。