--- 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`