---
# System prepended metadata

title: Builder 設計模式
tags: [Android Framework, Java 設計模式]

---

---
title: 'Builder 設計模式'
disqus: kyleAlien
---

Builder 設計模式
===

## Overview of Content

如有引用參考請詳註出處，感謝 :smile:

:::success
* 如果喜歡讀更好看一點的網頁版本，可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
    
    本篇文章對應的是 [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
:::

[TOC]

## Builder 使用場景

> 該模式是為了將 **複雜物件的建構解偶**，部件 & 組裝分離

* Builder 可進行客制化物件的建構，使其產生更細緻的不同物件（由 `Builder` 模式的使用者設定物件細節）

* Builder 的設計可以使的相同方法，**不同執行順序，產生不同的結果**

* 當初始化一個物件很複雜(**建構子需要的元素過多**)，且參數都有預設值 

:::success
* **Factory vs. Builder**

    兩者的相同點都在於產生一個產品給使用者用，但 **兩者關注重點不同**！
    
    * **Factory 則關注產品的生產（創建）**
    
    * **Builder 關注創建時的細節、順序**，算是利用了產品的生產結果，操作已生產的產品
:::

### Builder 定義 & Builder UML

* **Builder 定義**

    將一個複雜對象的 ++**建構**++ 與 ++**產品**（`Product`）**分離**++，使同樣的建構過程可以有不同的產品
    
    > 重點就在建構、產品的分離

* **Builder 角色 & UML 如下，有常見的兩種實作方案**

    1. **標準 Builder 介紹**

        | 描述 | 類 | 負責範圍 |
        | - | -------- | -------- |
        | abstract | `Builder` | 規範共用方法，實作由子類實現 |
        | - | `ConcreateBuilder` | Builder 的實作類，定義 Builder 實做 |
        | - | `Product` | Product 的實作類，定義 Product 實做 |
        | - | `Director` | 統一 Builder 組裝過程 |

        > ![](https://hackmd.io/_uploads/rJn6E94N3.png)

    2. 透過「**依賴倒置**」概念，**讓 Builder 依賴抽象，而不是實做**

        | 描述 | 類 | 負責範圍 |
        | - | -------- | -------- |
        | abstract | `Builder` | 規範共用方法，實作由子類實現 |
        | - | `ConcreateBuilder` | Builder 的實作類，定義 Builder 實做 |
        | abstract | `Product` | 產品抽象類，細節由依賴於抽象 (**依賴倒置**) |
        | - | `ConcreateProduct` | Product 的實作類，定義 Product 實做 |
        | - | `Director` | 統一 Builder 組裝過程 |

        > ![](https://hackmd.io/_uploads/BklqV5NNh.png)

### Builder 設計 - 優缺點

* **Builder 設計優點**
    
    * **封裝性**：使用者不必負擔過多的初始化，只須了解 Director 開出的方法（合約）即可

    * **方便控制細節**：在創建出物件之前可以在 Builder 檢查、修正
        
    * **產品類 `Product` 方便拓展** (橫向拓展)

* **Builder 設計缺點**

    * 設計的通病「類的增加」，請分析業務需求後，有這種需求在使用 Builder 模式，否則會增加類，同時增加複雜度

## Builder 實現

### Builder 標準

1. **`Builder` 抽象類**：建構共用的抽象方法 (可以用 `interface` or `abstract` 實現抽象) 並添加 **泛型**，方便拓展
    ```java=
    // Builder 類

    // 限制泛型必須是 Computer 的子類
    interface ComputerBuilder<T extends Computer> {

        ComputerBuilder<T> setBoard(String board);
        ComputerBuilder<T> setCPU(String cpu);
        ComputerBuilder<T> setPower(int power);
        ComputerBuilder<T> setMemory(int memory);

        String getBoard();
        String getCpu();
        int getPower();
        int getMemory();

        T create();
    }
    ```

2. **`ConcreateBuilder` 類**：實作 ComputerBuilder 接口，並且在內部可以有預設數值，也可以根據使用者的設定做修正

    ```java=
    // ConcreateBuilder 類

    public class Asus implements ComputerBuilder<Computer> {
        private String board = "技嘉", cpu = "AMD";
        private int power = 500, memory = 8;


        @Override
        public ComputerBuilder<Computer> setBoard(String board) {
            this.board = board;
            return this;
        }

        @Override
        public ComputerBuilder<Computer> setCPU(String cpu) {
            this.cpu = cpu;
            return this;
        }

        @Override
        public ComputerBuilder<Computer> setPower(int power) {
            this.power= power;
            return this;
        }

        @Override
        public ComputerBuilder<Computer> setMemory(int memory) {
            this.memory = memory;
            return this;
        }

        @Override
        public String getBoard() {
            return board;
        }

        @Override
        public String getCpu() {
            return cpu;
        }

        @Override
        public int getPower() {
            return power;
        }

        @Override
        public int getMemory() {
            return memory;
        }

        @Override
        public Computer create() {
            if(power < 200) {
                power = 200;
            }

            if(memory < 8) {
                memory = 8;
            }

            return new Computer(board, cpu, power, memory);
        }
        
    }
    ```
    :::info
    * **這裡回傳 `this` 是否符合最少知識原則呢**？
        
        如果是回傳自身物件，仍是符合 最少知識原則，因為使用這並無法透過回傳 `this` 來訪問到其他類
        
        如果回傳了其他類超過了兩層，這時就不符合原則
        
        > `create` 函數回傳了一層，還算是可接受範圍
    :::

3. **`Product` 類**：最終產生的 Computer 產品 (目標類)

    ```java=
    // Product 類

    public class Computer {
        
        private String board, cpu;
        private int power, memory;

        public Computer(String b, String c, int p, int m) {
            board = b;
            cpu = c;
            power = p;
            memory = m;
        }

        void setBoard(String board) {
            this.board = board;
        }

        void setCPU(String cpu) {
            this.cpu = cpu;
        }

        void setPower(int power) {
            this.power = power;
        }

        void setMemory(int memory) {
            this.memory = memory;
        }

        public void print() {
            System.out.println("CPU is " + cpu + "\n"
                    +"Board is " + board + "\n"
                    +"power is " + power + "w\n"
                    +"memory is " + memory + "G\n"
            );
        }
    }
    ```

    :::info
    * 該示例不包含 Product 依賴導致的部分
    :::

4. **`Director` 類**：**透過 Builder 類來創建各種不同的 Product 類**，以下創建一個高規格電腦、另一個普通電腦

    ```java=
    // Director 類

    public class Director {
        private final ComputerBuilder<?> computerBuilder;

        public Director(ComputerBuilder<?> computerBuilder) {
            this.computerBuilder = computerBuilder;
        }

        public Computer getAsusComputer_Normal() {
            return computerBuilder.create();     // 用 default 參數
        }

        public Computer getAsusComputer_Top() {
            return computerBuilder.setCPU("intel i9")
                    .setMemory(64)
                    .setPower(1200)
                    .create();
        }
    }
    ```


* **User 使用**：透過 ComputerBuilder 來組裝，不必關注細節，透過 create 方法就可以產生你需要的 Computer 類
    ```java=
    public class RunComputer {

        public static void main(String[] args) {

            Director director = new Director(new Asus());

            director.getAsusComputer_Normal().print();

            System.out.println("-----------------------------------------");

            director.getAsusComputer_Top().print();
        }

    }
    ```

    **--實做--**
    > ![](https://i.imgur.com/T4z3baY.png)
    
    

### Builder 變形 - 優化

* 由上面的範例可能出現幾個問題，我們可以做一些優化，但在優化前我們先來做些分析… 看看原先的設計哪裡有問題（或可以改善）
    
    * **`Product` 安全性**：如果不想讓使用者直接操作到 Product 類呢？
        
        > 可以透過 Java Package 的特性，將 Product 限制在 Package 內，並透過建構函數限制... 等等技術（請參考以下範例）
        
        :::info
        * 通常對於生產出的產品，我們也會希望它不會受到使用者 setting 而影響到產品的功能（就是生產出啥，使用者就用啥）；
        
            所以這次會把 Product 的 setter 功能移除，**這也可以加強 Product 安全性**
        :::
        
    * **簡化結構**：除去 `Director` 角色，這樣結構更加簡單
        
        > 但相對的，使用者需要直接操控到 Builder

* **優化過後的改動如下**

    1. **Builder 抽象類**：調整 ComputerBuilder_2 類，讓其有 getter 將子類的數據返回
        ```java=
        public interface ComputerBuilder_2<T extends Computer_2> {

            ComputerBuilder_2<T> setBoard(String board);
            ComputerBuilder_2<T> setCPU(String cpu);
            ComputerBuilder_2<T> setPower(int power);

            String getBoard();
            String getCpu();
            int getPower();

            T create();

        }
        ```

    2. **`ConcreateBuilder` 類**：實作類 Giga 並沒有太大的修改，重點是在 `create` 函數時，將自身（`this`）傳入，這樣 **可以簡化 Product 類的成員**
        ```java=
        // Builder 實作類

        public class Giga implements ComputerBuilder_2<Computer_2> {

            private String cpu;
            private String board;
            private int power;

            public Giga(String cpu, String board) {
                this.cpu = cpu;
                this.board = board;
            }

            @Override
            public ComputerBuilder_2<Computer_2> setBoard(String board) {
                this.board = board + "_version_2";
                return this;
            }

            @Override
            public ComputerBuilder_2<Computer_2> setCPU(String cpu) {
                this.cpu = cpu + "_version_2";
                return this;
            }

            @Override
            public ComputerBuilder_2<Computer_2> setPower(int power) {
                this.power = power;
                return this;
            }

            @Override
            public String getBoard() {
                return board;
            }

            @Override
            public String getCpu() {
                return cpu;
            }

            @Override
            public int getPower() {
                return power;
            }

            @Override
            public Computer_2 create() {
                if(power <= 0) {
                    this.power = 300;
                }
                
                // 傳入自身
                return new Computer_2(this);
            }
        }
        ```

    3. **`Product` 類**：修正目標 `Computer_2` 類別，其中有幾個方法做加強
        * **限制 Scope**：將 `Computer_2` 類移動到與 `ComputerBuilder_2` 相同資料夾，**透過 Package 限制其他類對 `Computer_2` 的訪問**

        * **限制建構函數**：設定 `Computer_2` constructor 建構函數，參數為 `ComputerBuilder_2`

        * **限制設定**：讓 **`Computer_2` 類單純化**，變成一個資料獲取點 (只有 getter)
            ```java=
            // Product

            public class Computer_2 {
                private String board;
                private String cpu;
                private int power;

                public Computer_2(ComputerBuilder_2<?> builder) {
                    board = builder.getBoard();
                    cpu = builder.getCpu();
                    power = builder.getPower();
                }

                void setBoard(String board) {
                    this.board = board;
                }

                void setCPU(String cpu) {
                    this.cpu = cpu;
                }

                void setPower(int power) {
                    this.power = power;
                }

                public void print() {
                    System.out.println("CPU is " + cpu + "\n"
                            +"Board is " + board + "\n"
                            +"power is " + power + "w\n"
                    );
                }
            }
            ```

            **--實做--**
            > ![](https://i.imgur.com/TfeQ6ji.png)
        
        針對變形 Builder 修正，UML 圖也需要做部分調整；可以看到 **Product 與 Builder 關係更加緊密 (聚合)**
        > ![](https://hackmd.io/_uploads/Hk0vliN43.png)




## Android Source Dialog 視窗
我們最常使用到的彈跳視窗 Dialog 就可以使用 Builder 模式建構

### Dialog 基礎使用

```java=
// Dialog.java  使用

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setAlertDialog();
    }

    private void setAlertDialog() {
        // Builder 創建 AlertDialog
        AlertDialog dialog = new AlertDialog.Builder(this)
                .setIcon(R.drawable.ic_launcher_background)
                .setMessage("Alert Dialog")
                .setTitle("Title")
                .setPositiveButton("Positive_Btn", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        setTitle("Positive_Btn_Click");
                    }
                })
                .setNeutralButton("Neutral_Btn", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        setTitle("Neutral_Btn_Click");
                    }
                })
                .setNegativeButton("Negative_Btn", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        setTitle("Negative_Btn_Click");
                    }
                })
                .show();
    }

}
```

### [AlertDialog](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/AlertDialog.java) 分析 - builder 變型

| 類 | 功能 | 對應腳色 |
| -------- | -------- | - |
| AlertDialog | 創建 AlertController 對象 | **Product** |
| AlertDialog#Builder | Builder 角色，會創建 AlertParams 類，並把設定存到其中 | **Builder** |
| AlertController#AlertParams | 內部儲存許多的屬性參數，透過 apply() 設定 AlertController 中的參數 | 儲存使用者對 Dialog 的設定 |
| AlertController | AlertParams 中設定的參數最終會轉嫁到 AlertController | 與 Window 通訊 |

> ![](https://i.imgur.com/berksxi.png)

* 首先分析最常使用的 `AlertDialog#Builder` 類
    
    1. Builder 會先創建一個 AlertParams 來保存使用者設定的數值
    
    2. AlertDialog 是透過 AlertDialog#Builder#create 方法創建
    
    3. AlertDialog 建構函數會創建 AlertController 類
    
    4. 最後將使用者設定從 AlertParams 複製到 AlertController 
        ```java=
        // AlertDialog.java

        public class AlertDialog extends Dialog implements DialogInterface {

            // 內部 Builder 類
            public static class Builder {
                // 參數儲存
                private final AlertController.AlertParams P;

                public Builder(Context context, int themeResId) {
                    // 創建參數倉庫 AlertParams
                    P = new AlertController.AlertParams(new ContextThemeWrapper(
                            context, resolveDialogTheme(context, themeResId)));
                }


                public Builder setTitle(@StringRes int titleId) {
                    P.mTitle = P.mContext.getText(titleId);
                    return this;
                }

                public Builder setCustomTitle(View customTitleView) {
                    P.mCustomTitleView = customTitleView;
                    return this;
                }

                public Builder setMessage(CharSequence message) {
                    P.mMessage = message;
                    return this;
                }

                public Builder setIcon(@DrawableRes int iconId) {
                    P.mIconId = iconId;
                    return this;
                }   

                ...

                // 創建目標物件，並設定到 AlerDialog 中
                public AlertDialog create() {
                    // Context has already been wrapped with the appropriate theme.
                    final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);

                    // @ 追蹤 apply 方法
                    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#create 方法創建 AlertDialog 對象 (new)，接著看 AlertDialog 的建構函數，知道它會創建一個 AlertController 類
        ```java=
        // AlertDialog.java

        public class AlertDialog extends Dialog implements DialogInterface {

            // AlierDialog 與 AlertController 產生關聯
            private AlertController mAlert;

            protected AlertDialog(Context context, @StyleRes int themeResId) {
                this(context, themeResId, true);
            }

            // 用 package 限制 constructor
            AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
                super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
                        createContextThemeWrapper);

                ...
                // 創建 AlertController
                mAlert = AlertController.create(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 void setView(View view) {
                mAlert.setView(view);
            }

            public void setIcon(Drawable icon) {
                mAlert.setIcon(icon);
            }

            ...

        }
        ```

    * [**AlertController**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/com/android/internal/app/AlertController.java)#Builder#create：透過  AlertParams#apply 將使用者設定，再次設定給 AlertController 對象
        ```java=
        // AlertController.java

        public class AlertController {

            /**
             * AlertController#AlertParams 類的 apply 方法
             * 將 AlertParams 參數設定到 AlertController 中
             */ 

            public static class AlertParams {

                // 將 User 設定在 AlertParams 的設定，賦予給 AlertController
                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);
                    }
                    
                    // 如果有 items 代表是一個列表 View
                    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](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/Dialog.java) 顯示畫面 show - 模板模式
* AlertDialog 最終要顯示畫面，會調用到 Dialog#**show** 方法
    
    > ![](https://i.imgur.com/Ynb626F.png)

    ```java=
    // Dialog.java 類

    public class Dialog implements DialogInterface, Window.Callback,
            KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {

        private final WindowManager mWindowManager;

        View mDecor;

        final Window mWindow;

        Dialog(@UiContext @NonNull Context context, @StyleRes int themeResId,
            boolean createContextThemeWrapper) {

            ... 準備 context

            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

            final Window w = new PhoneWindow(mContext);
            mWindow = w;

            ... 省略部份

            w.setWindowManager(mWindowManager, null, null);
            w.setGravity(Gravity.CENTER);

            mListenersHandler = new ListenersHandler(this);
        }

        public void show() {
            
            // 已經顯示，則直接返回
            if (mShowing) {
                if (mDecor != null) {
                    ...
                    mDecor.setVisibility(View.VISIBLE);
                }
                return;
            }

            mCanceled = false;

            if (!mCreated) {
                // onCreate 只會創建一次
                // @ 追蹤 dispatchOnCreate 方法
                dispatchOnCreate(null);
            } else {
                
                // 修正 configuration
                final Configuration config = mContext.getResources().getConfiguration();
                mWindow.getDecorView().dispatchConfigurationChanged(config);
            }

            // 每次顯示 Dialog 都會呼叫 onStart 方法
            onStart();
            mDecor = mWindow.getDecorView();

            ... 省略部份

            WindowManager.LayoutParams l = mWindow.getAttributes();
            
            // 當前有虛擬軟鍵盤，則修要調整 Dialog 顯示大小
            if ((l.softInputMode
                    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
                
                // 修正 WindowManager#LayoutParams
                WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
                nl.copyFrom(l);
                nl.softInputMode |=
                        WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
                l = nl;
            }

            // 3. 呼叫 Binder 通訊 讓 WMS#addView
            mWindowManager.addView(mDecor, l);
            mShowing = true;
        
            // 4. 發送 Message 到 Handle
            sendShowMessage();
        }
    
    }
    ```
    
    1. 在建構 Dialog 過程會依序呼叫幾個方法，第一個方法是 `dispatchOnCreate`，其實最後就會呼叫到 `onCreate` 方法 (這個方法相當於 Activity 的 `onCreate` 方法)，AlertDialog 就有 Override `onCreate` 方法
        ```java=
        // Dialog.java

        void dispatchOnCreate(Bundle savedInstanceState) {
            if (!mCreated) {
                onCreate(savedInstanceState);
                mCreated = true;
            }
        }

        protected void onCreate(Bundle savedInstanceState) {
        }

        // --------------------------------------------------------------
        // AlertDialog.java

        public class AlertDialog extends Dialog implements DialogInterface {

            private AlertController mAlert;

            void dispatchOnCreate(Bundle savedInstanceState) {
                if (!mCreated) {
                    onCreate(savedInstanceState);
                    mCreated = true;
                }
            }

            // 需要時可以覆寫 Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                // @ 追蹤 installContent 方法
                mAlert.installContent();
            }
        }
        ```
        :::info
        * **這其實類似一種模板模式**，加載 xml 行為由子類負責
            
            以 AlertDialog 來說就是透過 onCreate 方法，呼叫 AlertContoller 中的 PhoneWindow 加載 xml 布局
            
            > ![](https://i.imgur.com/BzIIsNQ.png)
        :::


    2. Dialog 子類可覆寫 Dialog#onStart 方法
        ```java=
        // Dialog.java

            protected void onStart() {
                if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
            }
        ```

    3. Dialog 通過 WindowManager 代理來與 WMS 通訊，目的是把 Dialog 加入視窗內 (addView)
        > ![](https://i.imgur.com/i6tB1Fg.png)

    4. 發送顯示消息
        ```java=
        private void sendShowMessage() {
                if (mShowMessage != null) {
                    // Obtain a new message so this dialog can be re-used
                    Message.obtain(mShowMessage).sendToTarget();
                }
            }
        ```
        
* 這裡我們重點分析 [**AlertDialog**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/AlertDialog.java)#onCreate 方法 (這個方法起初為 Dialog#dispatchOnCreate 呼叫)
    ```java=
    // AlertDialog.java

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mAlert.installContent();
        }
    ```
    * 分析 [**AlertController**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/com/android/internal/app/AlertController.java)#installContent 方法：該方法會透過 [**PhoneWindow**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java) 加載 xml 布局(預設是 [**alert_dialog**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/res/layout/alert_dialog.xml))
    
        ```java=
        // AlertDialog.java

            protected final Window mWindow;    // 目前是 PhoneWindow

            private int mAlertDialogLayout;


            protected AlertController(Context context, DialogInterface di, Window window) {

                // 預設布局
                mAlertDialogLayout = a.getResourceId(
                    R.styleable.AlertDialog_layout, R.layout.alert_dialog);

                ... 省略部份
            }

            public void installContent() {
                int contentView = selectContentView();

                // 加載 xml id
                mWindow.setContentView(contentView);

                // 初始化 AlertDialog 內容
                // @ 追蹤 setupView
                setupView();
            }

            // 選擇 layout view
            // 預設 xml 為 R.layout.alert_dialog
            private int selectContentView() {
                if (mButtonPanelSideLayout == 0) {
                    return mAlertDialogLayout;
                }
                if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
                    return mButtonPanelSideLayout;
                }
                // TODO: use layout hint side for long messages/lists
                return mAlertDialogLayout;
            }
        ```
        :::success
        * PhoneWindow#setContentView 布局加載分析：請參考 [**LayoutInflate 加載 Xml**](https://hackmd.io/EPfgb14TS-Kcd-Pv7tDZ-w?view#Activity-View)
        :::
    
    * 追蹤 AlertDialog#setUpView 方法：
    
        ```java=
        // AlertDialog.java

            // 設定 R.layout.alert_dialog 布局
            private void setupView() {
                final View parentPanel = mWindow.findViewById(R.id.parentPanel);
                // 預設 3 個區塊
                final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
                final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
                final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);

                // Install custom content before setting up the title or buttons so
                // that we can handle panel overrides.
                final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
                // 設定自定義 View
                setupCustomContent(customPanel);

                // 3 個區塊的 View
                final View customTopPanel = customPanel.findViewById(R.id.topPanel);
                final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
                final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);

                // Resolve the correct panels and remove the defaults, if needed.
                // 自定義 View 取代 default View
                final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
                final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
                final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);

                setupContent(contentPanel);
                setupButtons(buttonPanel);
                setupTitle(topPanel);

                ... 省略部份

                // 設定 ListView
                final ListView listView = mListView;
                if (listView != null && mAdapter != null) {
                    listView.setAdapter(mAdapter);
                    final int checkedItem = mCheckedItem;
                    if (checkedItem > -1) {
                        listView.setItemChecked(checkedItem, true);
                        listView.setSelection(checkedItem);
                    }
                }
            }
        ```
        `R.layout.alert_dialog` 布局概念圖
        > ![](https://i.imgur.com/0Q5kOHy.png)

* 時序圖
    > ![](https://i.imgur.com/SbvLjoh.png)

* AlertDialog 整體 UML 圖

    | 類 | 負責功能 |
    | -------- | -------- |
    | AlertDialog#Builder | 儲存使用者對 Dialog 的設定 |
    | AlertDialog | 透過 AlertController 控制布局加載 |
    | Dialog | 透過 WindowManager 添加 Dialog 視窗 |
    > ![](https://i.imgur.com/oUCKhNg.png)


## 更多的物件導向設計

物件導向的設計基礎如下，如果是初學者或是不熟悉的各位，建議可以從這些基礎開始認識，打好基底才能走個更穩（在學習的時候也需要不斷回頭看）！

:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)

* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)

* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::


### 創建模式 - Creation Patterns

* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)

* **創建模式 - `Creation Patterns`**：
    
    創建模式用於「**物件的創建**」，它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節，並提供創建對象的機制，例如單例模式、工廠模式… 等等，詳細解說請點擊以下連結

    :::success
    
    * [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)

    * [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)

    * [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)

    * [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
    
    * [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)

    * [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)

    * [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
    :::


### 行為模式 - Behavioral Patterns

* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)

* **行為模式 - `Behavioral Patterns`**：
    
    行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作，以完成特定任務。這些模式專注於改進物件之間的通信，從而提高系統的靈活性。例如，策略模式、觀察者模式… 等等，詳細解說請點擊以下連結

    :::warning
    * [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)

    * [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)

    * [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)

    * [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)

    * [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)

    * [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)

    * [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)

    * [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)

    * [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)

    * [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)

    * [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
    :::


### 結構模式 - Structural Patterns

* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)

* **結構模式 - `Structural Patterns`**：
    
    結構模式專注於「物件之間的組成」，以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時，不會破壞其整體結構。例如，外觀模式、代理模式… 等等，詳細解說請點擊以下連結

    :::danger
    * [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)

    * [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)

    * [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)

    * [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)

    * [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)

    * [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
    :::



## Appendix & FAQ

:::info 
:::

###### tags: `Java 設計模式` `基礎進階` `Android Framework`
