Android 设计模式学习之 Builder 模式
StaciaPcj
8年前
<p>建造者模式(Builder Pattern),是创造性模式之一,Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。</p> <h2>模式的使用场景</h2> <p>1.相同的方法,不同的执行顺序,产生不同的事件结果时;</p> <p>2.多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;</p> <p>3.产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。</p> <h2>UML类图</h2> <p style="text-align:center"><img src="https://simg.open-open.com/show/85eeca765be213ef8b20086b05a70c8a.jpg"></p> <p>角色介绍</p> <p>Product 产品类 : 产品的抽象类;</p> <p>Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程;</p> <p>ConcreteBuilder : 具体的构建器;</p> <p>Director : 统一组装过程(可省略)。</p> <h2>Builder模式简单实现</h2> <p>Builder模式最典型的例子,就是组装电脑的例子了</p> <p>创建产品类</p> <pre> <code class="language-java">public class Computer { private String mCpu; private String mRam; public void setmCpu(String mCpu) { this.mCpu = mCpu; } public void setmRam(String mRam) { this.mRam = mRam; } }</code></pre> <p>创建Builder类组装电脑有一套组装方法的模版,就是一个抽象的Builder类,里面提供了安装CPU、内存的方法,以及组装成电脑的create方法:</p> <pre> <code class="language-java">public abstract class Builder { public abstract void buildCpu(String cpu); public abstract void buildRam(String ram); public abstract Computer create(); }</code></pre> <p>实现了抽象的Builder类,ComputerBuilder类用于组装电脑:</p> <pre> <code class="language-java">public class ComputerBuilder extends Builder { private Computer mComputer = new Computer(); @Override public void buildCpu(String cpu) { mComputer.setmCpu(cpu); } @Override public void buildRam(String ram) { mComputer.setmRam(ram); } @Override public Computer create() { return mComputer; } }</code></pre> <p>用Dirextor指挥者类来统一组装过程</p> <pre> <code class="language-java">public class Direcror { Builder mBuild=null; public Direcror(Builder build){ this.mBuild=build; } public Computer CreateComputer(String cpu,String mainboard,String ram){ //规范建造流程 this.mBuild.buildMainboard(mainboard); this.mBuild.buildRam(ram); return mBuild.create(); } }</code></pre> <p>客户端调用指挥者类</p> <p>最后商家用指挥者类组装电脑。我们只需要提供我们想要的CPU,内存就可以了,至于商家怎样组装的电脑我们无需知道。</p> <pre> <code class="language-java">public class CreatComputer { public static void main(String[]args){ Builder mBuilder=new MoonComputerBuilder(); Direcror mDirecror=new Direcror(mBuilder); //组装电脑 mDirecror.CreateComputer("i5-3210","DDR4"); } }</code></pre> <h2>Android源码中的Builder模式</h2> <p>在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 :</p> <pre> <code class="language-java">//显示基本的AlertDialog private void showDialog(Context context) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setIcon(R.drawable.icon); builder.setTitle("头部"); builder.setMessage("内容"); builder.setPositiveButton("Button1", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { setTitle("点击了对话框上的Button1"); } }) .setNeutralButton("Button2", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { setTitle("点击了对话框上的Button2"); } }); builder.create().show(); // 构建AlertDialog, 并且显示 }</code></pre> <p>下面我们看看AlertDialog的相关源码</p> <pre> <code class="language-java">// AlertDialog public class AlertDialog extends Dialog implements DialogInterface { // Controller, 接受Builder成员变量P中的各个参数 private AlertController mAlert; // 构造函数 protected AlertDialog(Context context, int theme) { this(context, theme, true); } // 4 : 构造AlertDialog AlertDialog(Context context, int theme, boolean createContextWrapper) { super(context, resolveDialogTheme(context, theme), createContextWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = new AlertController(getContext(), this, getWindow()); } // 实际上调用的是mAlert的setTitle方法 @Override public void setTitle(CharSequence title) { super.setTitle(title); mAlert.setTitle(title); } // 实际上调用的是mAlert的setCustomTitle方法 public void setCustomTitle(View customTitleView) { mAlert.setCustomTitle(customTitleView); } public void setMessage(CharSequence message) { mAlert.setMessage(message); } // AlertDialog其他的代码省略 // ************ Builder为AlertDialog的内部类 ******************* public static class Builder { // 1 : 存储AlertDialog的各个参数, 例如title, message, icon等. 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; } // Builder的其他代码省略 ...... // 2 : 设置各种参数 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; } // 3 : 构建AlertDialog, 传递参数 public AlertDialog create() { // 调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); // 5 : 将P中的参数应用的dialog中的mAlert对象中 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; } } }</code></pre> <p>可以看到,通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看apply函数的实现 :</p> <pre> <code class="language-java">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); } // For a list, the client can either supply an array of items or an // adapter or a cursor 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); } } }</code></pre> <p>实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。在这里,Builder同时扮演了上文中提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。</p> <h2>在实际项目中的应用</h2> <p>我们可以采用系统已经提供好的Builder设计模式构建整个应用的万能Dialog,代码可以参考系统的AlertDialog</p> <pre> <code class="language-java">public static class Builder { private AlertController.AlertParams P; public Builder(Context context) { this(context, 0); } public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(); P.themeResId = themeResId; P.context = context; } public Builder setText(int viewId, CharSequence text) { P.textArray.put(viewId, text); return this; } public Builder setOnClickListener(int viewId, View.OnClickListener listener) { P.clickArray.put(viewId, listener); return this; } public Builder setContentView(int layoutId) { P.view = null; P.layoutId = layoutId; return this; } public Builder setContentView(View view) { P.layoutId = 0; P.view = view; return this; } /** * Sets whether the dialog is cancelable or not. Default is true. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setCancelable(boolean cancelable) { P.cancelable = cancelable; return this; } /** * Sets the callback that will be called if the dialog is canceled. * <p> * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than * being canceled or one of the supplied choices being selected. * If you are interested in listening for all cases where the dialog is dismissed * and not just when it is canceled, see * {@link #setOnDismissListener(OnDismissListener) setOnDismissListener}.</p> * * @return This Builder object to allow for chaining of calls to set methods * @see #setCancelable(boolean) * @see #setOnDismissListener(OnDismissListener) */ public Builder setOnCancelListener(OnCancelListener onCancelListener) { P.onCancelListener = onCancelListener; return this; } /** * Sets the callback that will be called when the dialog is dismissed for any reason. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setOnDismissListener(OnDismissListener onDismissListener) { P.onDismissListener = onDismissListener; return this; } /** * Sets the callback that will be called if a key is dispatched to the dialog. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setOnKeyListener(OnKeyListener onKeyListener) { P.onKeyListener = onKeyListener; return this; } /** * Creates an {@link AlertDialog} with the arguments supplied to this * builder. * <p/> * Calling this method does not display the dialog. If no additional * processing is needed, {@link #show()} may be called instead to both * create and display the dialog. */ public BaseDialog create() { // Context has already been wrapped with the appropriate theme. final BaseDialog dialog = new BaseDialog(P.context, P.themeResId); P.apply(dialog.mAlert); dialog.setCancelable(P.cancelable); if (P.cancelable) { dialog.setCanceledOnTouchOutside(true); } dialog.setOnCancelListener(P.onCancelListener); dialog.setOnDismissListener(P.onDismissListener); if (P.onKeyListener != null) { dialog.setOnKeyListener(P.onKeyListener); } return dialog; } /** * Creates an {@link AlertDialog} with the arguments supplied to this * builder and immediately displays the dialog. * <p/> * Calling this method is functionally identical to: * <pre> * AlertDialog dialog = builder.create(); * dialog.show(); * </pre> */ public BaseDialog show() { final BaseDialog dialog = create(); dialog.show(); return dialog; } }</code></pre> <pre> <code class="language-java">class AlertController { private DialogViewHelper mViewHelper; private BaseDialog mDialog; private Window mWindow; public AlertController(BaseDialog dialog, Window window) { mDialog = dialog; mWindow = window; } /** * 获取Dialog * @return */ public BaseDialog getDialog() { return mDialog; } /** * 获取window * @return */ public Window getWindow() { return mWindow; } public DialogViewHelper getViewHelper() { return mViewHelper; } /** * 设置View的辅助 * @param viewHelper */ public void setDialogViewHelper(DialogViewHelper viewHelper) { this.mViewHelper = viewHelper; } /** * 设置文本 * @param viewId * @param text */ public void setText(int viewId, CharSequence text) { mViewHelper.setText(viewId, text); } /** * 设置点击事件 * @param viewId * @param listener */ public void setOnClickListener(int viewId, View.OnClickListener listener) { mViewHelper.setOnClickListener(viewId, listener); } /** * 通过id获取View * @param viewId * @param <T> * @return */ public <T extends View> T getView(int viewId) { return mViewHelper.getView(viewId); } }</code></pre> <p>代码调用</p> <pre> <code class="language-java">@Override public void onClick(View v) { BaseDialog dialog = new BaseDialog.Builder(this) .setContentView(R.layout.detail_dialog).fullWith() .fromBottom(false) .show(); }</code></pre> <p>最后总结一下Buider模式的优缺点:</p> <p><strong>Builder 模式的优点:</strong></p> <p>1.将一个复杂对象的创建过程封装起来,使得客户端不必知道产品内部组成的细节;</p> <p>2.允许对象通过多个步骤来创建,并且可以改变过程和选择需要的过程;</p> <p>3.产品的实现可以被替换,因为客户端只看到一个抽象的接口;</p> <p>创建者独立,容易扩展。</p> <p><strong>Builder 模式缺点:</strong></p> <p>1.会产生多余的 Builder 对象以及 Director 对象,消耗内存;</p> <p>2.与工厂模式相比,采用 Builder 模式创建对象的客户,需要具备更多的领域知识。</p> <p> </p> <p>来自:http://blog.csdn.net/u012124438/article/details/59777619</p> <p> </p>