Android 设计模式学习之 Builder 模式

StaciaPcj 7年前
   <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>