建造者模式(Builder Pattern)

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可创建不同的表示。

使用场景

  • 相同的方法,不同的执行顺序,产生不同的结果。

  • 要初始化的对象十分复杂,如参数多且都具有默认值。

优缺点

优点:

  • 良好的封装性,可使调用者不必知道产品内部组成的细节。

  • 建造者独立,容易扩展。

缺点:

  • 会产生多余的Builder对象以及Director对象,消耗内存。

UML类图

建造者模式-w642

  • Product:产品类,产品的抽象基类。

  • Builder:Builder抽象基类,规范产品的组建步骤。

  • ConcreteBuilder:具体的Builder类。

  • Director:统一组装过程。

建造者模式的简单实现

用建造者模式实现一个Macbook的组装过程,涉及到的类有:

  • Computer:电脑,产品抽象基类。

  • Macbook:苹果笔记本,产品实现类。

  • Builder:Builder抽象基类。

  • MacbookBuilder:具体的Builder类。

  • Director:负责Computer的组装过程。

/* Computer类 */
public abstract class Computer {
    protected String mBoard;
    protected String mDisplay;
    protected String mOS;
    
    protected Computer() {}
    
    public void setBoard(String board) {
        mBoard = board;
    }
    
    public void setDisplay(String display) {
        mDisplay = display;
    }
    
    public void setOS(String os) {
        mOS = os;
    }
    
    @Override
    public String toString() {
        return "board = " + mBoard + ", display = " + mDisplay + ", os = " + mOS;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Macbook类 */
public class Macbook extends Computer {
    
}
1
2
3
4
/* Builder类 */
public abstract class Builder {
    public abstract void buildBoard(String board);
    
    public abstract void buildDisplay(String display);
    
    public abstract void buildOS(String os);
    
    public abstract Computer create();
}
1
2
3
4
5
6
7
8
9
10
/* MacbookBuilder类 */
public class MacbookBuilder extends Builder {
    private Computer mComputer = new Macbook();
    
    @Override
    public void buildBoard(String board) {
        mComputer.setBoard(board);
    }
    
    @Override
    public void buildDisplay(String display) {
        mComputer.setDisplay(display);
    }
    
    @Override
    public void buildOS(String os) {
        mComputer.setOS(os);
    }
    
    @Override
    public Computer create() {
        return mComputer;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Director类 */
public class Director {
    Builder mBuilder = null;
    
    public Director(Builder builder) {
        mBuilder = builder;
    }
    
    public void construct(String board, String display, String os) {
        mBuilder.buildBoard(board);
        mBuilder.buildDisplay(display);
        mBuilder.buildOS(os);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 测试代码 */
public class TestBuilder {
    
    public static void main(String[] args) {
        Builder builder = new MacbookBuilder();
        Director director = new Director(builder);
        director.construct("英特尔CPU", "Retina显示器", "Mac OS X 10.12");
        System.out.println("Computer Info: " + builder.create().toString());
    }
}
1
2
3
4
5
6
7
8
9
10

实际项目中的建造者模式

Android源码

Android并没有完全按照经典的建造者模式来实现,而是做了一些修改,使得建造者模式更易于使用。Android中的Builder类同时扮演了UML类图中的Builder、ConcreteBuilder和Director等角色。

在Android源码中,最常用到的建造者模式就是AlertDialog.Builder。使用该Builder来构建复杂的AlertDialog对象。

AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
    .setIcon(R.drawable.icon);
    .setTitle("title");
    .setMessage("msg");
    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
        }
    })
    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
        }
    })
    .create()
    .show();
1
2
3
4
5
6
7
8
9
10
11
12
13
14

以下通过分析AlertDialog、AlertDialog.Builder、AlertController和AlertController.AlertParams来分析Android中的建造者模式。

  1. 每个AlertDialog对象拥有一个AlertController类型的成员变量。

  2. 每个AlertDialog.Builder对象拥有一个AlertController.AlertParams类型的成员变量,用于存储各参数。

  3. 当调用AlertDialog.Builder对象的create方法时,会先实例化一个AlertDialog对象,然后将AlertParams成员变量中存储的各参数应用到这个AlertDialog对象的AlertController成员变量中。

  4. 当调用AlertDialog对象的show方法时,其内部最终调用AlertController成员变量的installContent方法。通过此方法来构建AlertDialog的视图,并将AlertDialog的DecorView添加到WindowManager中显示出来。

public class AlertDialog extends AppCompatDialog implements DialogInterface {
    // AlertController接收Builder成员变量P中的各个参数
    final AlertController mAlert;
    
    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());
    }
    
    public static class Builder {
        // 1. 存储AlertDialog的各个参数
        private final AlertController.AlertParams P;
        
        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;
        }
        
        // 2. 设置各种参数,并返回this利于流式编程。
        public Builder setTitle(@Nullable CharSequence title) {
            P.mTitle = title;
            return this;
        }
        
        // 3. 创建AlertDialog
        public AlertDialog create() {
            // 4. 实例化AlertDialog对象
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            // 5. 将Builder成员变量P中的各参数应用到dialog中的AlertController对象中。(见下面的AlertController.AlertParams类的apply方法)
            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;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class AlertController {
    public static class AlertParams {
        // 各参数的成员变量
    
        // 将各保存的参数应用到AlertController对象中。
        public void apply(AlertController dialog) {
            if (mCustomTitleView != null) {
                dialog.setCustomTitle(mCustomTitleView);
            } else {
                if (mTitle != null) {
                    dialog.setTitle(mTitle);
                }
                if (mIcon != null) {
                    dialog.setIcon(mIcon);
                }
                if (mIconId != 0) {
                    dialog.setIcon(mIconId);
                }
                
                // ...
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Android-Universal-Image-Loader

在UIL图片加载库中,图片加载器配置类ImageLoaderConfiguration使用了建造者模式。其实现方式与Android源码类似。

  • 内部Builder类充当Builder、ConcreteBuilder和Director等角色。

  • Builder类中的各setter都返回this,符合流式API的设计思想。

public final class ImageLoaderConfiguration {
    
    public static class Builder {
        // 各参数的成员变量
    
        public Builder(Context context) {
            this.context = context.getApplicationContext();
        }
        
        // 各参数的setter
        
        public ImageLoaderConfiguration build() {
            // 为各未被赋值的参数赋予默认值
            initEmptyFieldsWithDefaultValues();
            // 用自身存储的参数数据去初始化ImageLoaderConfiguration对象
            return new ImageLoaderConfiguration(this);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19