Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗

Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗

Overview of Content

如有引用參考請詳註出處,感謝

本文介紹了建構者模式在 Android Framework 中對話框視窗的應用

首先,我們會探討建構者模式的使用場景,並介紹建構者模式的定義和 UML 圖。接著,我們會討論建構者模式的設計,包括其優缺點以及不同的實現方式。

在具體實現部分我們會深入分析 Android Framework 中對話框視窗的原始碼,包括基礎使用方法、AlertDialog 的建構者模式變型,以及對話框顯示畫面的實現過程。

透過本文,讀者將了解建構者模式在 Android 開發中的應用,並學會如何運用這一設計模式來提高程式碼的可讀性和可維護性。


Builder 使用場景

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

● Builder 可進行客制化物件的建構,使其產生更細緻的不同物件(由 Builder 模式的使用者設定物件細節)

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

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

Factory vs. Builder

兩者的相同點都在於產生一個產品給使用者用,但 兩者關注重點不同

Factory 則關注產品的生產(創建)

Builder 關注創建時的細節、順序,算是利用了產品的生產結果,操作已生產的產品

Builder 定義 & Builder UML

Builder 定義

將一個複雜對象的 建構產品Product分離,使同樣的建構過程可以有不同的產品

重點就在建構、產品的分離

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

A. 標準 Builder 介紹

描述負責範圍
abstractBuilder規範共用方法,實作由子類實現
-ConcreateBuilderBuilder 的實作類,定義 Builder 實做
-ProductProduct 的實作類,定義 Product 實做
-Director統一 Builder 組裝過程

B. 透過「依賴倒置, DIP」概念,讓 Builder 依賴抽象,而不是實做

描述負責範圍
abstractBuilder規範共用方法,實作由子類實現
-ConcreateBuilderBuilder 的實作類,定義 Builder 實做
abstractProduct產品抽象類,細節由依賴於抽象 (依賴倒置)
-ConcreateProductProduct 的實作類,定義 Product 實做
-Director統一 Builder 組裝過程

Builder 設計:優缺點

Builder 設計優點

封裝性:使用者不必負擔過多的初始化,只須了解 Director 開出的方法(合約)即可

方便控制細節:在創建出物件之前可以在 Builder 檢查、修正

產品類 Product 方便拓展 (橫向拓展)

Builder 設計缺點

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


Builder 實現

Builder 標準

A. Builder 抽象類:建構共用的抽象方法 (可以用 interface or abstract 實現抽象) 並添加 泛型,方便拓展


// 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();
}

B. ConcreateBuilder:實作 ComputerBuilder 接口,並且在內部可以有預設數值,也可以根據使用者的設定做修正


// 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);
    }
    
}

這裡回傳 this 是否符合最少知識原則呢

如果是回傳自身物件,仍是符合 最少知識原則,因為使用這並無法透過回傳 this 來訪問到其他類

如果回傳了其他類超過了兩層,這時就不符合原則

create 函數回傳了一層,還算是可接受範圍

C. Product:最終產生的 Computer 產品 (目標類)


// 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"
        );
    }
}

● 該示例不包含 Product 依賴導致的部分

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


// 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 類


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

優化過後的改動如下

A. Builder 抽象類:調整 ComputerBuilder_2 類,讓其有 getter 將子類的數據返回


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();

}

B. ConcreateBuilder:實作類 Giga 並沒有太大的修改,重點是在 create 函數時,將自身(this)傳入,這樣 可以簡化 Product 類的成員


// 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);
    }
}

C. Product:修正目標 Computer_2 類別,其中有幾個方法做加強

限制 Scope:將 Computer_2 類移動到與 ComputerBuilder_2 相同資料夾,透過 Package 限制其他類對 Computer_2 的訪問

限制建構函數:設定 Computer_2 constructor 建構函數,參數為 ComputerBuilder_2

限制設定:讓 Computer_2 類單純化,變成一個資料獲取點 (只有 getter)


// 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 基礎使用


// 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 分析:builder 變型

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

● 首先分析最常使用的 AlertDialog#Builder

A. Builder 會先創建一個 AlertParams 來保存使用者設定的數值

B. AlertDialog 是透過 AlertDialog#Builder#create 方法創建

C. AlertDialog 建構函數會創建 AlertController 類

D. 最後將使用者設定從 AlertParams 複製到 AlertController


// 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 類


// 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#Builder#create:透過 AlertParams#apply 將使用者設定,再次設定給 AlertController 對象


// 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 顯示畫面 show:模板模式

● AlertDialog 最終要顯示畫面,會調用到 Dialog#show 方法


// 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();
    }

}

A. 在建構 Dialog 過程會依序呼叫幾個方法,第一個方法是 dispatchOnCreate,其實最後就會呼叫到 onCreate 方法 (這個方法相當於 Activity 的 onCreate 方法),AlertDialog 就有 Override onCreate 方法


// 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();
    }
}

這其實類似一種模板模式,加載 xml 行為由子類負責

以 AlertDialog 來說就是透過 onCreate 方法,呼叫 AlertContoller 中的 PhoneWindow 加載 xml 布局

B. Dialog 子類可覆寫 Dialog#onStart 方法


// Dialog.java

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

C. Dialog 通過 WindowManager 代理來與 WMS 通訊,目的是把 Dialog 加入視窗內 (addView)

D. 發送顯示消息


private void sendShowMessage() {
        if (mShowMessage != null) {
            // Obtain a new message so this dialog can be re-used
            Message.obtain(mShowMessage).sendToTarget();
        }
    }

● 這裡我們重點分析 AlertDialog#onCreate 方法 (這個方法起初為 Dialog#dispatchOnCreate 呼叫)


// AlertDialog.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAlert.installContent();
    }

● 分析 AlertController#installContent 方法:該方法會透過 PhoneWindow 加載 xml 布局(預設是 alert_dialog)


// 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;
    }

● PhoneWindow#setContentView 布局加載分析:請參考 LayoutInflate 加載 Xml

● 追蹤 AlertDialog#setUpView 方法:


// 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 視窗


更多的物件導向設計

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

設計建模 2 大概念- UML 分類、使用

物件導向設計原則 – 6 大原則(一)

物件導向設計原則 – 6 大原則(二)

創建、行為、結構型設計 8 個比較 | 包裝模式 | 最佳實踐

創建模式:Creation Patterns

創建模式 PK

創建模式 - Creation Patterns

結構模式:Structural Patterns

結構模式 PK

結構模式 - Structural Patterns

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

Bridge 橋接模式 | 解說實現 | 物件導向設計

Decorate 裝飾模式 | 解說實現 | 物件導向設計

Proxy 代理模式 | 解說實現 | 分析動態代理

Iterator 迭代設計 | 解說實現 | 物件導向設計

Facade 外觀、門面模式 | 解說實現 | 物件導向設計

Adapter 設計模式 | 解說實現 | 物件導向設計

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響