---
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 組裝過程 |
> 
2. 透過「**依賴倒置**」概念,**讓 Builder 依賴抽象,而不是實做**
| 描述 | 類 | 負責範圍 |
| - | -------- | -------- |
| abstract | `Builder` | 規範共用方法,實作由子類實現 |
| - | `ConcreateBuilder` | Builder 的實作類,定義 Builder 實做 |
| abstract | `Product` | 產品抽象類,細節由依賴於抽象 (**依賴倒置**) |
| - | `ConcreateProduct` | Product 的實作類,定義 Product 實做 |
| - | `Director` | 統一 Builder 組裝過程 |
> 
### 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();
}
}
```
**--實做--**
> 
### 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"
);
}
}
```
**--實做--**
> 
針對變形 Builder 修正,UML 圖也需要做部分調整;可以看到 **Product 與 Builder 關係更加緊密 (聚合)**
> 
## 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 通訊 |
> 
* 首先分析最常使用的 `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** 方法
> 
```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 布局
> 
:::
2. Dialog 子類可覆寫 Dialog#onStart 方法
```java=
// Dialog.java
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
}
```
3. Dialog 通過 WindowManager 代理來與 WMS 通訊,目的是把 Dialog 加入視窗內 (addView)
> 
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` 布局概念圖
> 
* 時序圖
> 
* AlertDialog 整體 UML 圖
| 類 | 負責功能 |
| -------- | -------- |
| AlertDialog#Builder | 儲存使用者對 Dialog 的設定 |
| AlertDialog | 透過 AlertController 控制布局加載 |
| Dialog | 透過 WindowManager 添加 Dialog 視窗 |
> 
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::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`