设计模式七 Builder模式

来源:互联网 发布:觉醒字幕组知乎 编辑:程序博客网 时间:2024/05/18 20:11

农历2015年的最后一篇blog。

该篇为《Android 源码设计模式 解析与实践》Builder设计模式的读书笔记

Builder模式是什么?
Builder模式如同其名,其是一步一步创建一个复杂对象的创建型模式。该模式可以将一个复杂的对象与它的表示分离:做到同样的构建过程可以有不同的表示。

我们在什么场景下使用该模式:
当初始化一个对象特别复杂,参数多,且很多参数都具有默认值的时候,可以使用Builder模式。

Builder模式在日常Android开发当中,也是被我们经常用到,几乎每个Android程序员都用到过,只是自己不知道当前的代码已经蕴含了Builder模式在里面:

下面记录一段现在手中项目当中的一段代码:

PhotoGraph.Builder builder = new PhotoGraph.Builder(request.getContext());builder.setEventTime(event.getTmGen())       .setLiftNumber(liftNo)       .setPriority(PhotoGraphConfig.LIFT_FAULT_PRIORITY)       .setZipFileName(zipFileName)       .setOnPhotoListener(new OnPhotoListener() {        @Override        public void openCameraFailure() {            //...        }        @Override        public void onCompletion(String filePath) {            //...        }    });TaskPhotoGraph.getInstance().put(builder.create());

该代码片段就使用了Builder模式,下面在给出我们经常使用的AlertDialog代码:

AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("提示")       .setMessage("Message...")       .setPositiveButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                // ....            }        }).setNegativeButton("取消", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                // ...            }        });builder.show();

ok,上面的代码片段就是我们经常要使用AlertDialog的实现,其实从类名就能看出是一个Builder模式,通过builder对象去组装AlertDialog的各个参数(如上面的Title、Message、PositiveButton、NegativeButton等),将AlertDialog的表示与构造进行了分离。

那既然知道了Builder模式是怎么个样子,下面就来分析分析Builder模式的实现,这里以AlertDialog的源码作为示例:

public class AlertDialog extends Dialog implements DialogInterface {    private AlertController mAlert; // mAlert 接受Builder成员变量p中各个参数    // ...省略一些代码    @Override    public void setTitle(CharSequence title) {        super.setTitle(title);        mAlert.setTitle(title);    }    public void setMessage(CharSequence message) {        mAlert.setMessage(message);    }    public static class Builder {        // 存储 AlertDialog的各个参数,用于创建AlertDialog的时候传递给AlertDialog的成员变量mAlert        private final AlertController.AlertParams P;        // ...省略一些代码        public Builder(Context context) {            this(context, resolveDialogTheme(context, 0));        }        // 设置各种参数                public Builder setTitle(int titleId) {            P.mTitle = P.mContext.getText(titleId);            return this;        }        public Builder setMessage(int messageId) {            P.mMessage = P.mContext.getText(messageId);            return this;        }        public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {            P.mPositiveButtonText = text;            P.mPositiveButtonListener = listener;            return this;        }        public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {            P.mNegativeButtonText = text;            P.mNegativeButtonListener = listener;            return this;        }    }        public AlertDialog create() {            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);            // 将Builder中p的参数传递应用到AlertDialog中的mAlert对象中            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;        }  }

以上代码是从android.app.AlertDialog.java 类中截取的代码片。
从上述代码中,我们能知道的是:

  • Builder类可以设置AlertDialog中需要用到的一切参数
  • Builder 成员变量AlertController.AlertParams p用来存储AlertDialog待用的参数
  • AlertDialog中的成员变量 mAlert对象中包含了与p一 一对应的成员变量
  • Builder的create()方法中,会调用 p.apply(dialog.mAlert) 方法将p中早先设置的参数一 一传递到AlertDialog 的 mAlert 对象中

到这里为止,我们为Builder设置的一系列参数,已经全部由Builder 的成员变量AlertController.AlertParams p转移到了AlertDialog成员变量 mAlert对象中。这里我们不禁有一点疑惑了:

为什么传递这一系列参数要这么麻烦,要用到AlertController.AlertParam 和 AlertController 这两个对象去交接?为什么不在Builder类中一系列setXXX方法中将参数直接赋值给mAlert对应的成员变量
解释这一点很简单。这里可以仔细看看Builder类的声明为静态内部类,众所周知:java语法中静态类方法中无法引用非静态成员变量,也无法调用非静态方法。

而上面为什么可以使用AlertController.AlertParams p变量去存储设置给Builder的参数呢,答案很简单,因为在AlertController类中AlertParams类也是一个静态内部类。

以上就是Builder模式的一个具体实现。

现在接着往下看:AlertDialog现在只是拿到了这些参数(因为AlertDialog拥有AlertController 类型成员变量 mAlert ),后面在哪里使用到了这些参数呢?我们看看AlertDialog的show()方法

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

AlertDialog的show()方法实际上又是调用了Dialog的show()方法(AlertDialog 继承 Dialog)

public void show() {        if (mShowing) { // 如果已经显示,直接return            if (mDecor != null) {                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);                }                mDecor.setVisibility(View.VISIBLE);            }            return;        }        mCanceled = false;        if (!mCreated) {            // 1、调用onCreate()            dispatchOnCreate(null);        }        // 2、调用onStart()        onStart();        // 3、获取DecorView        mDecor = mWindow.getDecorView();        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {            final ApplicationInfo info = mContext.getApplicationInfo();            mWindow.setDefaultIcon(info.icon);            mWindow.setDefaultLogo(info.logo);            mActionBar = new WindowDecorActionBar(this);        }        // 4、获取布局参数        WindowManager.LayoutParams l = mWindow.getAttributes();        if ((l.softInputMode                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();            nl.copyFrom(l);            nl.softInputMode |=                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;            l = nl;        }        try {            // 5、将mDecor添加到WindowManager中            mWindowManager.addView(mDecor, l);            mShowing = true;           // 6、发送显示Dialog的消息            sendShowMessage();        } finally {        } }

上面代码已经添加了注释,主要也就是做了注释中的6件事,从这6件事当中,我们能看到一些关于Dialog生命周期的方法,根据以往经验(回想下Activity的onCreate()方法中的setContentView(R.layout.activity_xxx_xxx);),一般在onCreate()方法中去构建AlertDialog视图内容。

上面注释1中调用了dispatchOnCreate()方法

void dispatchOnCreate(Bundle savedInstanceState) {    if (!mCreated) {        onCreate(savedInstanceState);        mCreated = true;    }}

dispatchOnCreate()方法中又调用了onCreate()方法,onCreate()方法在Dialog中是一个空实现

protected void onCreate(Bundle savedInstanceState) {}

上面已经说过AlertDialog继承Dialog那么具体实现应该在AlertDialog中

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    mAlert.installContent();}

onCreate()方法中我们一眼看到,这里终于又回到了使用了mAlert对象(忘记了mAlert对象的,请回过头看下)

public void installContent() {    // 设置窗口没有title栏    mWindow.requestFeature(Window.FRATURE_NO_TITLE);    // 设置窗口视图布局    mWindow.setContentView(mAlertDialogLayout); //mAlertDialogLayout是默认的窗口视图布局对象    // 设置窗口的其它子视图内容    setupView();}

这里调用mAlert的installContent()方法,在该方法中主要做了两件事:

  1. 调用Window对象的setContentView()方法(这里setContentView()同Activity的setContentView(),实际上Activity的setContentView()最终也是调用的Window对象setContentView())。因此这里就是设置AlertDialog布局的视图内容。

  2. 调用setupView(),为AlertDialog设置各项参数(即title、message 、icon、button等这些内容)

到此,我们在最开始使用Builder模式构建的一系列参数在setupView()方法中都得到了应用。

现在再回到之前AlertDialog的show()方法那里

if (!mCreated) {   // 1、调用onCreate()   dispatchOnCreate(null);}// ...省略一些代码// 3、获取DecorViewmDecor = mWindow.getDecorView();// ...省略一些代码// 5、将mDecor添加到WindowManager中mWindowManager.addView(mDecor, l);mShowing = true;// 6、发送显示Dialog的消息sendShowMessage();

setupView()结束后,我们的视图内容填充完毕,视图布局也全部设置完成,并且关联到了Window对象,即mWindow拥有这个视图内容以及布局。到这里注释1的方法执行完毕,接着通过注释3、5、6的代码,显而易见:

WindowManager会将Window对象的DecorView(即之前关联到Window对象的视图内容和视图布局)添加到用户窗口上,并显示出来。用户也就看到了弹出来的AlertDialog。

以上就是对Builder设计模式的读书笔记。主要涉及到了:

  1. Builder设计模式的实现
  2. AlertDialog源码解析

同时如果弄清楚了AlertDialog源码,那么现在自定义自己的Dialog也是非常轻松的。

以上如有纰漏,不吝指出!

0 0
原创粉丝点击