1. 程式人生 > >建造者模式及在Andorid中的應用

建造者模式及在Andorid中的應用

1. 建造者模式概述

建造者模式(Builder Pattern):將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。建造者模式是一種物件建立型模式。

建造者模式一步一步建立一個複雜的物件,它允許使用者只通過指定複雜物件的型別和內容就可以構建它們,使用者不需要知道內部的具體構建細節。建造者模式結構如圖所示:
這裡寫圖片描述

  • Builder(抽象建造者):它為建立一個產品Product物件的各個部件指定抽象介面,在該介面中一般宣告兩類方法,一類方法是buildPartX(),它們用於建立複雜物件的各個部件;另一類方法是getResult(),它們用於返回複雜物件。Builder既可以是抽象類,也可以是介面。
  • ConcreteBuilder(具體建造者):它實現了Builder介面,實現各個部件的具體構造和裝配方法,定義並明確它所建立的複雜物件,也可以提供一個方法返回建立好的複雜產品物件。
  • Product(產品角色):它是被構建的複雜物件,包含多個組成部件,具體建造者建立該產品的內部表示並定義它的裝配過程。
  • Director(指揮者):指揮者又稱為導演類,它負責安排複雜物件的建造次序,指揮者與抽象建造者之間存在關聯關係,可以在其construct()建造方法中呼叫建造者物件的部件構造與裝配方法,完成複雜物件的建造

產品角色是我們最終要建立的物件,一個產品裡面有很多組成部分,典型程式碼如下:
這裡寫圖片描述


抽象建造者定義了產品的各個組建的建立方法,典型程式碼如下:
這裡寫圖片描述
具體建造者繼承於抽象建造者並實現了具體的各個組建的建立方式,典型程式碼如下:
這裡寫圖片描述
指揮者隔離了客戶和產品的建立過程,實際控制了產品的建立流程,典型程式碼如下:
這裡寫圖片描述
對於客戶端而言,只需關心具體的建造者即可,一般情況下,客戶端類程式碼片段如下所示:
這裡寫圖片描述

2. 建造者模式在Andorid的AlertDialog中的應用

平常我們基本是這樣使用AlertDialog的,程式碼如下:

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("標題"
); builder.setMessage("確定提交嗎?"); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.show();

為什麼我們不能直接new一個AlertDialog呢,檢視原始碼發現:

    protected AlertDialog(@NonNull Context context) {
        this(context, 0);
    }

    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, resolveDialogTheme(context, themeResId));
        mAlert = new AlertController(getContext(), this, getWindow());
    }

    protected AlertDialog(@NonNull Context context, boolean cancelable,
            @Nullable OnCancelListener cancelListener) {
        this(context, 0);
        setCancelable(cancelable);
        setOnCancelListener(cancelListener);
    }

AlertDialog的構造方法全是protected的,只能通過AlertDilog.Builder.create()構建,檢視AlertDilog.Builder原始碼主要如下:

    private final AlertController.AlertParams P;
    private final int mTheme;
    public Builder(@NonNull Context context, @StyleRes int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
            mTheme = themeResId;
    }
    public Builder setTitle(@Nullable CharSequence title) {
            P.mTitle = title;
            return this;
    }
    public Builder setCustomTitle(@Nullable View customTitleView) {
            P.mCustomTitleView = customTitleView;
            return this;
    }
    public Builder setMessage(@Nullable CharSequence message) {
        P.mMessage = message;
        return this;
    }
    ......
    public AlertDialog create() {
           // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
           // so we always have to re-set the theme
           final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
           P.apply(dialog.mAlert);
           dialog.setCancelable(P.mCancelable);
           if (P.mCancelable) {
               dialog.setCanceledOnTouchOutside(true);
           }
           dialog.setOnCancelListener(P.mOnCancelListener);
           dialog.setOnDismissListener(P.mOnDismissListener);
           if (P.mOnKeyListener != null) {
               dialog.setOnKeyListener(P.mOnKeyListener);
           }
           return dialog;
    }
    public AlertDialog show() {
         final AlertDialog dialog = create();
         dialog.show();
         return dialog;
    }

可以看出Builder相當於建造者角色,AlertController.AlertParams相當於產品角色,Builder提供的一系列set方法都是為了改變AlertController.AlertParams的屬性,在create()方法裡,第25行 p.apply(dialog.mAlert), 檢視apply方法原始碼如下:

    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);
          }
          // 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);
              }
          } else if (mViewLayoutResId != 0) {
              dialog.setView(mViewLayoutResId);
          }

          /*
          dialog.setCancelable(mCancelable);
          dialog.setOnCancelListener(mOnCancelListener);
          if (mOnKeyListener != null) {
              dialog.setOnKeyListener(mOnKeyListener);
          }
          */
      }

apply方法主要將AlertParams的屬性通過AlertController重新設定一遍,AlertDialog的屬性實際上還是通過AlertController控制,檢視AlertDialog的set方法程式碼,主要如下:

    final AlertController mAlert;
    @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);
    }

可以看出,AlertDialog的屬性都是通過AlertController控制,客戶不知道具體的建立細節,都是通過AlertController構建,AlertDialog充當指揮者的角色。