一、模式定义

造者模式(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对象,消耗内存;
  • 对象的构建过程暴露。