设计模式七 Builder模式

本文深入探讨了Builder设计模式的应用及其实现方式,并以AlertDialog为例详细解析了其源码,展示了如何利用Builder模式简化复杂对象的构建过程。

农历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中

@Override
protected 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、获取DecorView
mDecor = 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也是非常轻松的。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值