通过示例和源码阐述建造者模式

2018-12-05  本文已影响0人  付凯强

0. 序言

1. 介绍

2. 场景

3. UML类图(用PC观看)

建造者模式类图.png

角色介绍:
① Product产品类——产品的抽象类。
② Builder——抽象Builder类,规范产品的组件。
③ ConcreteBuilder——具体的Builder类,实现具体的组件过程。
④ Director——统一组装过程

4. 场景一实现示例

以跳舞为例:

public abstract class Dance {
    protected abstract void hand();
    protected abstract void foot();
    protected abstract void turn_around();
}
public class Hiphop extends Dance {

    private List<String> mPerformList = new ArrayList<>();

    @Override
    protected void hand() {
        mPerformList.add("街舞:动动手");
    }

    @Override
    protected void foot() {
        mPerformList.add("街舞:动动脚");
    }

    @Override
    protected void turn_around() {
        mPerformList.add("街舞:转身");
    }

    public List<String> getPerformList() {
        return mPerformList;
    }
}

说明:为了能看出不同的跳舞顺序,我们增加了一个List集合和访问List集合的方法。

public abstract class Builder {
    public abstract void hand();
    public abstract void foot();
    public abstract void turn_around();
    public abstract Dance create();
}
public class DanceBuilder extends Builder {

    Dance mDance = new Hiphop();

    @Override
    public void hand() {
        mDance.hand();
    }

    @Override
    public void foot() {
        mDance.foot();
    }

    @Override
    public void turn_around() {
        mDance.turn_around();
    }

    @Override
    public Dance create() {
        return mDance;
    }
}
public class Director {
    Builder mBuilder = null;

    public Director(Builder builder) {
        mBuilder = builder;
    }

    // 可以定义不同的顺序
    public void perform(){
        mBuilder.hand();
        mBuilder.foot();
        mBuilder.turn_around();
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Builder builder = new DanceBuilder();
        Director director = new Director(builder);
        director.perform();
        Log.i("fukq","跳舞的顺序是:"+((Hiphop)(builder.create())).getPerformList());
    }
}
12-05 16:18:52.241 30387-30387/com.smartisan.builder I/fukq: 跳舞的顺序是:[街舞:动动手, 街舞:动动脚, 街舞:转身]

说明:
① 通过DanceBuilder来构建Dance对象,而Director封装了构建复杂产品对象的过程,而DanceBuilder中有跳舞的不同的顺序,展示了场景一:相同的方法,不同的顺序,带来不同的结果。
② 开发中,Director会被省略,直接使用Builder来进行对象的组装,我们去掉Director类,修改下DanceBuilder类:

public abstract class Builder {
    public abstract Dance setHand();
    public abstract Dance setFoot();
    public abstract Dance setTurn_around();
}
public class DanceBuilder extends Builder {

    Dance mDance = new Hiphop();

    @Override
    public Dance setHand() {
        mDance.hand();
        return mDance;
    }

    @Override
    public Dance setFoot() {
        mDance.foot();
        return mDance;
    }

    @Override
    public Dance setTurn_around() {
        mDance.turn_around();
        return mDance;
    }
}

说明:建造者模式通常用Builder进行链式调用(为了呈现链式调用这里修改下方法名),它的关键点在于每个setter方法都返回自身,代码我们可以这样写:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Dance dance = new DanceBuilder().setTurn_around().setFoot().setHand().create();
        Log.i("fukq","跳舞的顺序是:"+ ((Hiphop)dance).getPerformList());
    }
}

说明:通过链式调用,随意对顺序进行编辑,得到复杂对象Dance。

5. 场景二实现示例:

以AlertDialog为例:

           new AlertDialog.Builder(this)
                .setIcon(R.mipmap.ic_launcher)
                .setTitle("Title")
                .setMessage("Messages")
                .setPositiveButton("Button01",new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

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

                    }
                }).setNeutralButton("Button03", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

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

说明:通过类名Builder以及链式调用我们可以看出来它是一个建造者模式,通过Builder来组装Dialog的各个部分,我们分析下源码,再次验证下:

public class AlertDialog extends AppCompatDialog implements DialogInterface {
    final AlertController mAlert;  // 1
    ...
    protected AlertDialog(@NonNull Context context) {
        this(context, 0);
    }

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

    protected AlertDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
        this(context, 0);
        this.setCancelable(cancelable);
        this.setOnCancelListener(cancelListener);
    }
    ...
    public void setTitle(CharSequence title) {
        super.setTitle(title);
        this.mAlert.setTitle(title);
    }

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

    public void setMessage(CharSequence message) {
        this.mAlert.setMessage(message);
    }
   ...
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.mAlert.installContent();
    }
   ...
    public static class Builder { // 3
        private final AlertParams P;
        private final int mTheme;

        public Builder(@NonNull Context context) {
            this(context, AlertDialog.resolveDialogTheme(context, 0));
        }

        public Builder(@NonNull Context context, @StyleRes int themeResId) {
            this.P = new AlertParams(new ContextThemeWrapper(context, AlertDialog.resolveDialogTheme(context, themeResId)));
            this.mTheme = themeResId;
        }

        @NonNull
        public Context getContext() {
            return this.P.mContext;
        }

        public AlertDialog.Builder setTitle(@StringRes int titleId) {
            this.P.mTitle = this.P.mContext.getText(titleId);
            return this;
        }

        public AlertDialog.Builder setTitle(@Nullable CharSequence title) {
            this.P.mTitle = title;
            return this;
        }

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

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

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

       ...
        public AlertDialog.Builder setView(View view) {
            this.P.mView = view;
            this.P.mViewLayoutResId = 0;
            this.P.mViewSpacingSpecified = false;
            return this;
        }
        ...
        public AlertDialog create() { // 4
            AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);
            this.P.apply(dialog.mAlert);
            dialog.setCancelable(this.P.mCancelable);
            if (this.P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }

            dialog.setOnCancelListener(this.P.mOnCancelListener);
            dialog.setOnDismissListener(this.P.mOnDismissListener);
            if (this.P.mOnKeyListener != null) {
                dialog.setOnKeyListener(this.P.mOnKeyListener);
            }

            return dialog;
        }

        public AlertDialog show() {
            AlertDialog dialog = this.create();
            dialog.show();
            return dialog;
        }
    }
}

说明:
① 省略号的地方省略一些代码。
② 整个的AlertDialog大分为构造方法、set方法和内部类Builder。
③ 分析源码的顺序是:从调用开始的地方,查看涉及到的所有代码的源码,再来看下调用的代码:

 new AlertDialog.Builder(this)
                .setTitle("Title")
                ...
                .create().show();

所以这里查看源码的顺序是:先看AlertDialog的内部类Builder,然后再看setTitle方法,再看create方法,最后看show方法。
④ .Builder(this) 的源码:

        public Builder(@NonNull Context context) {
            this(context, AlertDialog.resolveDialogTheme(context, 0));
        }

说明:这里只是传入了上下文this。
⑤ .setTitle("Title")的源码:

private final AlertParams P;
        public AlertDialog.Builder setTitle(@Nullable CharSequence title) {
            this.P.mTitle = title;
            return this;
        }

说明:给Title赋值,职责赋值给了AlertParams P中的title。
⑥ .create()的源码:

        public AlertDialog create() {
            AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);
            this.P.apply(dialog.mAlert);
            dialog.setCancelable(this.P.mCancelable);
            if (this.P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }

            dialog.setOnCancelListener(this.P.mOnCancelListener);
            dialog.setOnDismissListener(this.P.mOnDismissListener);
            if (this.P.mOnKeyListener != null) {
                dialog.setOnKeyListener(this.P.mOnKeyListener);
            }

            return dialog;
        }

说明:这些代码里面,最可能和title相关的只剩下 this.P.apply(dialog.mAlert); 这一句,所以我们看下这个apply方法的源码:

 public void apply(AlertController dialog) {
            if (this.mCustomTitleView != null) {
                dialog.setCustomTitle(this.mCustomTitleView);
            } else {
                if (this.mTitle != null) {
                    dialog.setTitle(this.mTitle);
                }
                ...
            }
            ...
        }

说明:apply这个方法是在AlertParams类中,dialog.mAlert 指的就是AlertController,所以apply的作用就是把AlertParams P中的字段值都赋值给AlertController的字段值。
⑦ show()方法从源码来看都是关于如何把Dialog显示在屏幕上的内容,不再附代码,不再分析。
⑧ 通过以上分析我们可以看到,当需要多个参数的时候,可以通过builder模式来传递需要的参数,而且兼容多种参数配置,就像kotlin中的默认参数所带来的便利一样。

6. 后续

如果大家喜欢这篇文章,欢迎点赞!
如果想看更多 设计模式 方面的技术,欢迎关注!

上一篇下一篇

猜你喜欢

热点阅读