模式简介 《Effective java》一书2.2节介绍了当类的构造器包含多个参数时常用的一种构造器模式——Builder模式。不直接生成对象,而是利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后在builder对象上调用类似setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法生成不可变的对象。这个builder是它构建的类的静态成员类。
《Effective java》中给出的完整示例如下:
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 53 54 55 56 57 58 59 60 61 62 63 64 public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder { private final int servingSize; private final int servings; private int calories = 0 ; private int fat = 0 ; private int sodium = 0 ; private int carbohydrate = 0 ; public Builder (int servingSize, int servings) { this .servingSize = servingSize; this .servings = servings; } public Builder calories (int val) { calories = val; return this ; } public Builder fat (int val) { fat = val; return this ; } public Builder sodium (int val) { sodium = val; return this ; } public Builder carbohydrate (int val) { carbohydrate = val; return this ; } public NutritionFacts build () { return new NutritionFacts(this ); } } private NutritionFacts (Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } public static void main (String[] args) { NutritionFacts cocaCola = new NutritionFacts.Builder(240 , 8 ). calories(100 ).sodium(35 ).carbohydrate(27 ).build(); } }
Builder模式十分灵活,可以利用单个Builder构建多个对象。 Builder的参数可以在创建对象期间进行调整,也可以随不同对象而改变。 builder可以自动填充某些域,例如每次创建对象时自动生成序列号。
Android源码中相关实现 在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。 举个例子:
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 private void showDialog (Context context) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setIcon(R.drawable.icon); builder.setTitle("Title" ); builder.setMessage("Message" ); builder.setPositiveButton("Button1" , new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int whichButton) { setTitle("点击了对话框上的Button1" ); } }); builder.setNeutralButton("Button2" , new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int whichButton) { setTitle("点击了对话框上的Button2" ); } }); builder.setNegativeButton("Button3" , new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int whichButton) { setTitle("点击了对话框上的Button3" ); } }); builder.create().show(); }
下面我们看看AlertDialog的相关源码 :
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 public class AlertDialog extends Dialog implements DialogInterface { private AlertController mAlert; protected AlertDialog (Context context, int theme) { this (context, theme, true ); } AlertDialog(Context context, int theme, boolean createContextWrapper) { super (context, resolveDialogTheme(context, theme), createContextWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = new AlertController(getContext(), this , getWindow()); } @Override public void setTitle (CharSequence title) { super .setTitle(title); mAlert.setTitle(title); } public void setCustomTitle (View customTitleView) { mAlert.setCustomTitle(customTitleView); } public void setMessage (CharSequence message) { mAlert.setMessage(message); } public static class Builder { private final AlertController.AlertParams P; * Constructor using a context for this builder and the {@link AlertDialog} it creates. */ public Builder (Context context) { this (context, resolveDialogTheme(context, 0 )); } public Builder (Context context, int theme) { P = new AlertController.AlertParams(new ContextThemeWrapper( context, resolveDialogTheme(context, theme))); mTheme = theme; } public Builder setTitle (CharSequence title) { P.mTitle = title; return this ; } public Builder setMessage (CharSequence message) { P.mMessage = message; return this ; } public Builder setIcon (int iconId) { P.mIconId = iconId; return this ; } public Builder setPositiveButton (CharSequence text, final OnClickListener listener) { P.mPositiveButtonText = text; P.mPositiveButtonListener = listener; return this ; } public Builder setView (View view) { P.mView = view; P.mViewSpacingSpecified = false ; return this ; } public AlertDialog create () { final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false ); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); if (P.mCancelable) { dialog.setCanceledOnTouchOutside(true ); } dialog.setOnCancelListener(P.mOnCancelListener); if (P.mOnKeyListener != null ) { dialog.setOnKeyListener(P.mOnKeyListener); } return dialog; } } }
可以看到,通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。 在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。 我们看看apply函数的实现 :
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 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); } if (mIconAttrId > 0 ) { dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); } } if (mMessage != null ) { dialog.setMessage(mMessage); } if (mPositiveButtonText != null ) { dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText, mPositiveButtonListener, null ); } if (mNegativeButtonText != null ) { dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText, mNegativeButtonListener, null ); } if (mNeutralButtonText != null ) { dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText, mNeutralButtonListener, null ); } if (mForceInverseBackground) { dialog.setInverseBackgroundForced(true ); } if ((mItems != null ) || (mCursor != null ) || (mAdapter != null )) { createListView(dialog); } if (mView != null ) { if (mViewSpacingSpecified) { dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom); } else { dialog.setView(mView); } } }
实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。 从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。
优缺点 优点
良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节;
建造者独立,容易扩展;
在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
缺点
会产生多余的Builder对象以及Director对象,消耗内存;
对象的构建过程暴露。