• 一、模式定义
  • 二、模式结构
  • 三、时序图
  • 四、简单实现
  • 五、Android源码中模式实现
  • 六、优缺点
    • 优点
    • 缺点

    一、模式定义

    造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。根据中文翻译的不同,建造者模式又可以称为生成器模式。

    二、模式结构

    建造者模式包含如下角色:

    • Builder:抽象建造者
    • ConcreteBuilder:具体建造者
    • Director:指挥者
    • Product:产品角色

    建造者模式 - 图1

    三、时序图

    建造者模式 - 图2

    四、简单实现

    电脑的组装过程较为复杂,步骤繁多,但是顺序却是不固定的。下面我们以组装电脑为例来演示一下简单且经典的builder模式

    1. package com.dp.example.builder;
    2. /**
    3. * Computer产品抽象类, 为了例子简单, 只列出这几个属性
    4. *
    5. * @author mrsimple
    6. *
    7. */
    8. public abstract class Computer {
    9. protected int mCpuCore = 1;
    10. protected int mRamSize = 0;
    11. protected String mOs = "Dos";
    12. protected Computer() {
    13. }
    14. // 设置CPU核心数
    15. public abstract void setCPU(int core);
    16. // 设置内存
    17. public abstract void setRAM(int gb);
    18. // 设置操作系统
    19. public abstract void setOs(String os);
    20. @Override
    21. public String toString() {
    22. return "Computer [mCpuCore=" + mCpuCore + ", mRamSize=" + mRamSize
    23. + ", mOs=" + mOs + "]";
    24. }
    25. }
    26. package com.dp.example.builder;
    27. /**
    28. * Apple电脑
    29. */
    30. public class AppleComputer extends Computer {
    31. protected AppleComputer() {
    32. }
    33. @Override
    34. public void setCPU(int core) {
    35. mCpuCore = core;
    36. }
    37. @Override
    38. public void setRAM(int gb) {
    39. mRamSize = gb;
    40. }
    41. @Override
    42. public void setOs(String os) {
    43. mOs = os;
    44. }
    45. }
    46. package com.dp.example.builder;
    47. package com.dp.example.builder;
    48. /**
    49. * builder抽象类
    50. *
    51. */
    52. public abstract class Builder {
    53. // 设置CPU核心数
    54. public abstract void buildCPU(int core);
    55. // 设置内存
    56. public abstract void buildRAM(int gb);
    57. // 设置操作系统
    58. public abstract void buildOs(String os);
    59. // 创建Computer
    60. public abstract Computer create();
    61. }
    62. package com.dp.example.builder;
    63. public class ApplePCBuilder extends Builder {
    64. private Computer mApplePc = new AppleComputer();
    65. @Override
    66. public void buildCPU(int core) {
    67. mApplePc.setCPU(core);
    68. }
    69. @Override
    70. public void buildRAM(int gb) {
    71. mApplePc.setRAM(gb);
    72. }
    73. @Override
    74. public void buildOs(String os) {
    75. mApplePc.setOs(os);
    76. }
    77. @Override
    78. public Computer create() {
    79. return mApplePc;
    80. }
    81. }
    82. package com.dp.example.builder;
    83. public class Director {
    84. Builder mBuilder = null;
    85. /**
    86. *
    87. * @param builder
    88. */
    89. public Director(Builder builder) {
    90. mBuilder = builder;
    91. }
    92. /**
    93. * 构建对象
    94. *
    95. * @param cpu
    96. * @param ram
    97. * @param os
    98. */
    99. public void construct(int cpu, int ram, String os) {
    100. mBuilder.buildCPU(cpu);
    101. mBuilder.buildRAM(ram);
    102. mBuilder.buildOs(os);
    103. }
    104. }
    105. /**
    106. * 经典实现较为繁琐
    107. *
    108. * @author mrsimple
    109. *
    110. */
    111. public class Test {
    112. public static void main(String[] args) {
    113. // 构建器
    114. Builder builder = new ApplePCBuilder();
    115. // Director
    116. Director pcDirector = new Director(builder);
    117. // 封装构建过程, 4核, 内存2GB, Mac系统
    118. pcDirector.construct(4, 2, "Mac OS X 10.9.1");
    119. // 构建电脑, 输出相关信息
    120. System.out.println("Computer Info : " + builder.create().toString());
    121. }
    122. }

    五、Android源码中模式实现

    在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 :

    1. //显示基本的AlertDialog
    2. private void showDialog(Context context) {
    3. AlertDialog.Builder builder = new AlertDialog.Builder(context);
    4. builder.setIcon(R.drawable.icon);
    5. builder.setTitle("Title");
    6. builder.setMessage("Message");
    7. builder.setPositiveButton("Button1",
    8. new DialogInterface.OnClickListener() {
    9. public void onClick(DialogInterface dialog, int whichButton) {
    10. setTitle("点击了对话框上的Button1");
    11. }
    12. });
    13. builder.setNeutralButton("Button2",
    14. new DialogInterface.OnClickListener() {
    15. public void onClick(DialogInterface dialog, int whichButton) {
    16. setTitle("点击了对话框上的Button2");
    17. }
    18. });
    19. builder.setNegativeButton("Button3",
    20. new DialogInterface.OnClickListener() {
    21. public void onClick(DialogInterface dialog, int whichButton) {
    22. setTitle("点击了对话框上的Button3");
    23. }
    24. });
    25. builder.create().show(); // 构建AlertDialog, 并且显示
    26. }

    结果 : result

    下面我们看看AlertDialog的相关源码 :

    1. // AlertDialog
    2. public class AlertDialog extends Dialog implements DialogInterface {
    3. // Controller, 接受Builder成员变量P中的各个参数
    4. private AlertController mAlert;
    5. // 构造函数
    6. protected AlertDialog(Context context, int theme) {
    7. this(context, theme, true);
    8. }
    9. // 4 : 构造AlertDialog
    10. AlertDialog(Context context, int theme, boolean createContextWrapper) {
    11. super(context, resolveDialogTheme(context, theme), createContextWrapper);
    12. mWindow.alwaysReadCloseOnTouchAttr();
    13. mAlert = new AlertController(getContext(), this, getWindow());
    14. }
    15. // 实际上调用的是mAlert的setTitle方法
    16. @Override
    17. public void setTitle(CharSequence title) {
    18. super.setTitle(title);
    19. mAlert.setTitle(title);
    20. }
    21. // 实际上调用的是mAlert的setCustomTitle方法
    22. public void setCustomTitle(View customTitleView) {
    23. mAlert.setCustomTitle(customTitleView);
    24. }
    25. public void setMessage(CharSequence message) {
    26. mAlert.setMessage(message);
    27. }
    28. // AlertDialog其他的代码省略
    29. // ************ Builder为AlertDialog的内部类 *******************
    30. public static class Builder {
    31. // 1 : 存储AlertDialog的各个参数, 例如title, message, icon等.
    32. private final AlertController.AlertParams P;
    33. // 属性省略
    34. /**
    35. * Constructor using a context for this builder and the {@link AlertDialog} it creates.
    36. */
    37. public Builder(Context context) {
    38. this(context, resolveDialogTheme(context, 0));
    39. }
    40. public Builder(Context context, int theme) {
    41. P = new AlertController.AlertParams(new ContextThemeWrapper(
    42. context, resolveDialogTheme(context, theme)));
    43. mTheme = theme;
    44. }
    45. // Builder的其他代码省略 ......
    46. // 2 : 设置各种参数
    47. public Builder setTitle(CharSequence title) {
    48. P.mTitle = title;
    49. return this;
    50. }
    51. public Builder setMessage(CharSequence message) {
    52. P.mMessage = message;
    53. return this;
    54. }
    55. public Builder setIcon(int iconId) {
    56. P.mIconId = iconId;
    57. return this;
    58. }
    59. public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
    60. P.mPositiveButtonText = text;
    61. P.mPositiveButtonListener = listener;
    62. return this;
    63. }
    64. public Builder setView(View view) {
    65. P.mView = view;
    66. P.mViewSpacingSpecified = false;
    67. return this;
    68. }
    69. // 3 : 构建AlertDialog, 传递参数
    70. public AlertDialog create() {
    71. // 调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog
    72. final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
    73. // 5 : 将P中的参数应用的dialog中的mAlert对象中
    74. P.apply(dialog.mAlert);
    75. dialog.setCancelable(P.mCancelable);
    76. if (P.mCancelable) {
    77. dialog.setCanceledOnTouchOutside(true);
    78. }
    79. dialog.setOnCancelListener(P.mOnCancelListener);
    80. if (P.mOnKeyListener != null) {
    81. dialog.setOnKeyListener(P.mOnKeyListener);
    82. }
    83. return dialog;
    84. }
    85. }
    86. }

    可以看到,通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看apply函数的实现 :

    1. public void apply(AlertController dialog) {
    2. if (mCustomTitleView != null) {
    3. dialog.setCustomTitle(mCustomTitleView);
    4. } else {
    5. if (mTitle != null) {
    6. dialog.setTitle(mTitle);
    7. }
    8. if (mIcon != null) {
    9. dialog.setIcon(mIcon);
    10. }
    11. if (mIconId >= 0) {
    12. dialog.setIcon(mIconId);
    13. }
    14. if (mIconAttrId > 0) {
    15. dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
    16. }
    17. }
    18. if (mMessage != null) {
    19. dialog.setMessage(mMessage);
    20. }
    21. if (mPositiveButtonText != null) {
    22. dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
    23. mPositiveButtonListener, null);
    24. }
    25. if (mNegativeButtonText != null) {
    26. dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
    27. mNegativeButtonListener, null);
    28. }
    29. if (mNeutralButtonText != null) {
    30. dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
    31. mNeutralButtonListener, null);
    32. }
    33. if (mForceInverseBackground) {
    34. dialog.setInverseBackgroundForced(true);
    35. }
    36. // For a list, the client can either supply an array of items or an
    37. // adapter or a cursor
    38. if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
    39. createListView(dialog);
    40. }
    41. if (mView != null) {
    42. if (mViewSpacingSpecified) {
    43. dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
    44. mViewSpacingBottom);
    45. } else {
    46. dialog.setView(mView);
    47. }
    48. }
    49. }

    实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。在这里,Builder同时扮演了上文中提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。

    六、优缺点

    优点

    • 良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节;
    • 建造者独立,容易扩展;
    • 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。

    缺点

    • 会产生多余的Builder对象以及Director对象,消耗内存;
    • 对象的构建过程暴露。