Android设计模式学习——Builder模式

有的类带有许多设置。可能有点抽象,举例来说:

  1. 一个 Dialog,在创建的时候要设置 Title、Icon、按钮等等。
  2. 一个网络框架(例如 Retrofit)在创建时也需要设置 baseUrl、ConverterFactory、CallAdapterFactory 等等。

如何满足这一需求,我能想出下面这几种方法:

设置一堆构造函数

以 Dialog 为例,我可以来一堆:

public Dialog(Context context) {...}
public Dialog(Context context, String title) {...}
public Dialog(Context context, String message) {...}
public Dialog(Context context, String message, Button positiveButton) {...}
...

写了四个我就写不动了,为什么?太麻烦了。用排列组合一算,如果有3个可选项,我就得写8个构造函数。

设置 setter

还是以 Dialog 为例,我给出一个最简单的构造函数,然后每个可选项都给一个 setter:

public Dialog(Context context) {//各选项都设个默认值}
public void setTitle(String t) {...}
public void setIcon(int res) {...}
public void setMessage(String m) {...}
public void setPositiveButton(Button b) {...}
...

这样在创建对象的时候,需要什么就给什么,没给的都采用默认值:

Dialog d = new Dialog(this);
d.setTitle("Maxiee");
d.setMessage("http://judymax.com");
d.show();

这个看起来挺好的了,但是还能否更简洁逼格一些呢?那就是实用 Builder 模式

Builder 模式

在上一节的基础之上,在 Dialog 中添加一个内部类 Builder:

public Dialog(Context context) {//各选项都设个默认值}
public void setTitle(String t) {...}
public void setIcon(int res) {...}
public void setMessage(String m) {...}
public void setPositiveButton(Button b) {...}
...
// 本节新添加
public class Builder {
    private Dialog mDialog;
    
    public Builder(Context context) {
        mDialog = new Dialog(context);
    }
    
    public Builder setTitle(String t) {
        mDialog.setTitle(t);
        return this;
    }
    
    public Builder setMessage(String m) {
        mDialog.setMessage(String m);
        return this;
    }
    ...
    public Dialog create() { return mDialog;}
}

这样,在使用的时候就可以很帅气地:

new Dialog.Builder(this)
        .setTitle("Maxiee")
        .setMessage("http://judymax.com")
        .create()
        .show()

这样就形成了一个链式调用,一气呵成。

我想的这些思路是否正确呢?来看看 Android 中的 AlertDialog 是如何处理的。

AlertDialog 中的 Builder 模式

我们直接来看 AlertDialog 的内部类 Builder。

第一个不同是,Builder 有一个成员:

private final AlertController.AlertParams P;

它是专门用来存放 build 过程中设置的参数的。上一节中,我们在 builder 的构造函数中直接创建 Dialog,然后链式调用 setter 对 Dialog 进行设置。在这里,builder 的构造函数中创建的是一个保存参数的的示例,链式调用 setter 是对这个参数赋值,到最后 create 的时候才创建 AlertDialog 实例:

public AlertDialog create() {
  final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
  // 用参数设置 Dialog
  P.apply(dialog.mAlert);
  ...
 }

AlertDialog 的 builder 里面也是一堆 setter,其内容就是向 P 这个参数成员赋值。实现都大致相同,随便拿出一个看看:

public Builder setPositiveButton(int textId, final OnClickListener listener) {
  P.mPositiveButtonText = P.mContext.getText(textId);
  P.mPositiveButtonListener = listener;
  return this;
}

最后有一点我发现,AlertDialog 自己又一个 show() 方法(继承自 Dialog 类),AlertDialog 的 Builder 类也有一个 show() 方法。

首先,链式调用最后的那个 show() 调用的是哪个?是 AlertDialog 的。因为 create() 方法返回的类型是 AlertDialog。

那么问题就来了,Builder 里面这个 show() 是干什么用的?看看它的实现:

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

它原来是把 create().show() 这一段给封装了,这一段可以简化为一个 show() 😂:

new AlertDialog.Builder(this)
        .setA(...)
        .setB(...)
        .show();