Builder 模式

最近工作之余一直在斷斷續(xù)續(xù)的研究媒體選擇庫,在 GitHub 上搜了好多庫對比看了看,在學(xué)習(xí)研究過程中發(fā)現(xiàn)其中都運(yùn)用了 Builder模式,今天就一起學(xué)習(xí)一下 Builder模式,順便看看它在 Android 源碼中的應(yīng)用。

我們在實(shí)際開發(fā)中,必然會遇到一些需求需要構(gòu)建一個十分復(fù)雜的對象,譬如本人最近開發(fā)的項(xiàng)目中就需要構(gòu)建一個媒體庫選擇器,類似微信和眾多app中都有的圖片、視屏資源選擇器。這個選擇器(Selector)是相對比較復(fù)雜的,它需要很多屬性,比如:

  • 媒體資源類型: 圖片/視屏
  • 選擇模式: 單選/多選
  • 多選上限
  • 是否支持預(yù)覽
  • 是否支持裁剪
  • 選擇器UI風(fēng)格樣式
  • ......

通常我們可以通過構(gòu)造函數(shù)的參數(shù)形式去寫一個實(shí)現(xiàn)類

Selector(int type)

Selector(int type, int model)

Selector(int type, int model, int maxSize)

Selector(int type, int model, int maxsize, boolean isPreview)

......

再或者可以使用 getter 和 setter 方法去設(shè)置各個屬性

public class Selector {
    private int type; // 媒體資源類型: 圖片/視屏
    private int model; // 選擇模式: 單選/多選
    private int maxSize; // 多選上限
    private boolean isPreview; // 是否支持預(yù)覽
    
    private ...... // 更多屬性就不一一舉例了,大家腦部一下    

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public int getModel() {
        return model;
    }

    public void setModel(int model) {
        this.model = model;
    }

    public int getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    public boolean isPreview() {
        return isPreview;
    }

    public void setPreview(boolean isPreview) {
        this.isPreview = isPreview;
    }

    .......
}

先分析一下這兩種構(gòu)建對象的方式:

第一種方式通過重載構(gòu)造方法實(shí)現(xiàn),在參數(shù)不多的情況下,是比較方便快捷的,一旦參數(shù)多了,就會產(chǎn)生大量的構(gòu)造方法,代碼可讀性大大降低,并且難以維護(hù),對調(diào)用者來說也是一種災(zāi)難;

第二種方法可讀性不錯,也易于維護(hù),但是這樣子做對象會產(chǎn)生不確定性,當(dāng)你構(gòu)造 Selector 時想要傳入全部參數(shù)的時候,那你就必需將所有的 setter 方法調(diào)用完成之后才算創(chuàng)建完成。然而一部分的調(diào)用者看到了這個對象后,以為這個對象已經(jīng)創(chuàng)建完畢,就直接使用了,其實(shí) Selector 對象并沒有創(chuàng)建完成,另外,這個 Selector 對象也是可變的,不可變類的所有好處也將隨之消散。

寫到這里其實(shí)大家已經(jīng)想到了,肯定有更好的辦法去解決這個問題,是的,你猜對了, 今天我們的主角 Builder模式 就是為解決這類問題而生的。下面我們一起看看 Builder模式 是如何優(yōu)雅的處理這類尷尬的。

模式的定義

將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。

模式的使用場景

  1. 相同的方法,不同的執(zhí)行順序,產(chǎn)生不同的事件結(jié)果時;
  2. 多個部件或零件,都可以裝配到一個對象中,但是產(chǎn)生的運(yùn)行結(jié)果又不相同時;
  3. 產(chǎn)品類非常復(fù)雜,或者產(chǎn)品類中的調(diào)用順序不同產(chǎn)生了不同的效能,這個時候使用建造者模式非常合適;

化解上述尷尬的過程

Builder模式 屬于創(chuàng)建型,一步一步將一個復(fù)雜對象創(chuàng)建出來,允許用戶在不知道內(nèi)部構(gòu)建細(xì)節(jié)的情況下,可以更精細(xì)地控制對象的構(gòu)造流程。還是上面同樣的需求,使用 Builder模式 實(shí)現(xiàn)如下:

public class Selector {
    private final int type; // 媒體資源類型: 圖片/視屏
    private final int model; // 選擇模式: 單選/多選
    private final int maxSize; // 多選上限
    private final boolean isPreview; // 是否支持預(yù)覽
    
    private final ...... // 更多屬性就不一一舉例了,大家腦部一下
    
    // 私有構(gòu)造方法
    private Selector(SelectorBuilder selectorBuilder){
    this.type = selectorBuilder.type;
    this.model = selectorBuilder.model;
    this.maxSize = selectorBuilder.maxSize;
    this.isPreview = selectorBuilder.isPreview;
    ......

    /** 由于所有屬性都是 final 修飾的,所以只提供 getter 方法 **/

    public int getType() {
        return type;
    }

    public int getModel() {
        return model;
    }

    public int getMaxSize() {
        return maxSize;
    }

    public boolean isPreview() {
        return isPreview;
    }

    .......
    
    // Builder 方法
    public static class SelectorBuilder{
        private int type; // 媒體資源類型: 圖片/視屏
        private int model; // 選擇模式: 單選/多選
        private int maxSize; // 多選上限
        private boolean isPreview; // 是否支持預(yù)覽
        
        private ...... // 更多屬性就不一一舉例了,大家腦部一下
        public SelectorBuilder() {
            // 設(shè)置各個屬性的默認(rèn)值
            this.type = 1;
            this.model = 1;
            this.maxSize = 9;
            this.isPreview = true;
            ......
        }

        public SelectorBuilder setType(int type){
            this.type = type;
            return this;
        }

        public SelectorBuilder setModel(int model) {
            this.model = model;
            return this;
        }

        public SelectorBuilder setMaxSize(int maxSize) {
            this.maxSize = maxSize;
            return this;
        }

        ...... // 全部的setter方法就省略了

        public Selector build() {
            return new Selector(this);
        }
    }
}

值得注意的是 Selector 的構(gòu)造方法是私有的,并且所有屬性都是 final 修飾的,是不可變屬性,對調(diào)用者也只提供 getter 方法,SelectorBuilder 內(nèi)部類可以根據(jù)調(diào)用者的具體需求隨意接收任意多個參數(shù),應(yīng)為我們再 SelectorBuilder 的構(gòu)造方法中為每一個參數(shù)都設(shè)置了默認(rèn)值,即使調(diào)用者調(diào)用時漏傳某個參數(shù),也不會影響整個創(chuàng)建過程。當(dāng)我們將我們需用的所有參數(shù)傳入后,隨后調(diào)用 build() 構(gòu)造 Selector 對象,代碼如下:

new Selector.SelectorBuilder()
            .setType(2)
            .setModel(2)
            .setMaxSize(3)
            . ......
            .build();

是不是很簡潔?意不意外?驚不驚喜?你沒看錯,就是這么厲害!

Android源碼中的模式實(shí)現(xiàn)

在Android源碼中,我們最常用到的Builder模式就是AlertDialog.Builder, 使用該Builder來構(gòu)建復(fù)雜的AlertDialog對象。簡單示例如下 :

// 顯示基本的AlertDialog  
private void showDialog(Context context) {  
    AlertDialog.Builder builder = new AlertDialog.Builder(context);  
    builder.setIcon(R.drawable.icon);  
    builder.setTitle("Title");  
    builder.setMessage("Message");  
    builder.setPositiveButton("Button1",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("點(diǎn)擊了對話框上的Button1");  
                }  
            });  
    builder.setNeutralButton("Button2",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("點(diǎn)擊了對話框上的Button2");  
                }  
            });  
    builder.setNegativeButton("Button3",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("點(diǎn)擊了對話框上的Button3");  
                }  
            });  
    builder.create().show();  // 構(gòu)建AlertDialog, 并且顯示
} 

優(yōu)缺點(diǎn)

當(dāng)然在代碼世界,永遠(yuǎn)沒有絕對的完美,我們只是在走向完美的道路上盡力去填補(bǔ)一個個坑而已。 Builder模式 有它的好處,給我們帶來了方便,但同時也會犧牲一些美好,這是不可避免的。

優(yōu)點(diǎn)
  • 良好的封裝性, 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié);
  • 建造者獨(dú)立,容易擴(kuò)展;
  • 在對象創(chuàng)建過程中會使用到系統(tǒng)中的一些其它對象,這些對象在產(chǎn)品對象的創(chuàng)建過程中不易得到。
缺點(diǎn)
  • 會產(chǎn)生多余的Builder對象,消耗內(nèi)存;
  • 對象的構(gòu)建過程暴露。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容