Builder 模式

最近工作之余一直在斷斷續(xù)續(xù)的研究媒體選擇庫靖榕,在 GitHub 上搜了好多庫對比看了看矗愧,在學習研究過程中發(fā)現其中都運用了 Builder模式峻贮,今天就一起學習一下 Builder模式,順便看看它在 Android 源碼中的應用哩都。

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

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

通常我們可以通過構造函數的參數形式去寫一個實現類

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 方法去設置各個屬性

public class Selector {
    private int type; // 媒體資源類型: 圖片/視屏
    private int model; // 選擇模式: 單選/多選
    private int maxSize; // 多選上限
    private boolean isPreview; // 是否支持預覽
    
    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;
    }

    .......
}

先分析一下這兩種構建對象的方式:

第一種方式通過重載構造方法實現,在參數不多的情況下植阴,是比較方便快捷的蟹瘾,一旦參數多了,就會產生大量的構造方法掠手,代碼可讀性大大降低憾朴,并且難以維護,對調用者來說也是一種災難喷鸽;

第二種方法可讀性不錯众雷,也易于維護,但是這樣子做對象會產生不確定性做祝,當你構造 Selector 時想要傳入全部參數的時候砾省,那你就必需將所有的 setter 方法調用完成之后才算創(chuàng)建完成。然而一部分的調用者看到了這個對象后混槐,以為這個對象已經創(chuàng)建完畢编兄,就直接使用了,其實 Selector 對象并沒有創(chuàng)建完成声登,另外狠鸳,這個 Selector 對象也是可變的,不可變類的所有好處也將隨之消散捌刮。

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

模式的定義

將一個復雜對象的構建與它的表示分離个少,使得同樣的構建過程可以創(chuàng)建不同的表示洪乍。

模式的使用場景

  1. 相同的方法,不同的執(zhí)行順序夜焦,產生不同的事件結果時壳澳;
  2. 多個部件或零件,都可以裝配到一個對象中茫经,但是產生的運行結果又不相同時巷波;
  3. 產品類非常復雜,或者產品類中的調用順序不同產生了不同的效能卸伞,這個時候使用建造者模式非常合適抹镊;

化解上述尷尬的過程

Builder模式 屬于創(chuàng)建型,一步一步將一個復雜對象創(chuàng)建出來荤傲,允許用戶在不知道內部構建細節(jié)的情況下垮耳,可以更精細地控制對象的構造流程。還是上面同樣的需求遂黍,使用 Builder模式 實現如下:

public class Selector {
    private final int type; // 媒體資源類型: 圖片/視屏
    private final int model; // 選擇模式: 單選/多選
    private final int maxSize; // 多選上限
    private final boolean isPreview; // 是否支持預覽
    
    private final ...... // 更多屬性就不一一舉例了终佛,大家腦部一下
    
    // 私有構造方法
    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; // 是否支持預覽
        
        private ...... // 更多屬性就不一一舉例了雾家,大家腦部一下
        public SelectorBuilder() {
            // 設置各個屬性的默認值
            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 的構造方法是私有的铃彰,并且所有屬性都是 final 修飾的,是不可變屬性榜贴,對調用者也只提供 getter 方法豌研,SelectorBuilder 內部類可以根據調用者的具體需求隨意接收任意多個參數,應為我們再 SelectorBuilder 的構造方法中為每一個參數都設置了默認值唬党,即使調用者調用時漏傳某個參數鹃共,也不會影響整個創(chuàng)建過程。當我們將我們需用的所有參數傳入后驶拱,隨后調用 build() 構造 Selector 對象霜浴,代碼如下:

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

是不是很簡潔?意不意外蓝纲?驚不驚喜阴孟?你沒看錯,就是這么厲害税迷!

Android源碼中的模式實現

在Android源碼中永丝,我們最常用到的Builder模式就是AlertDialog.Builder, 使用該Builder來構建復雜的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("點擊了對話框上的Button1");  
                }  
            });  
    builder.setNeutralButton("Button2",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("點擊了對話框上的Button2");  
                }  
            });  
    builder.setNegativeButton("Button3",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("點擊了對話框上的Button3");  
                }  
            });  
    builder.create().show();  // 構建AlertDialog慕嚷, 并且顯示
} 

優(yōu)缺點

當然在代碼世界,永遠沒有絕對的完美,我們只是在走向完美的道路上盡力去填補一個個坑而已喝检。 Builder模式 有它的好處嗅辣,給我們帶來了方便,但同時也會犧牲一些美好挠说,這是不可避免的澡谭。

優(yōu)點
  • 良好的封裝性, 使用建造者模式可以使客戶端不必知道產品內部組成的細節(jié)损俭;
  • 建造者獨立蛙奖,容易擴展;
  • 在對象創(chuàng)建過程中會使用到系統中的一些其它對象杆兵,這些對象在產品對象的創(chuàng)建過程中不易得到外永。
缺點
  • 會產生多余的Builder對象,消耗內存拧咳;
  • 對象的構建過程暴露。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末囚灼,一起剝皮案震驚了整個濱河市骆膝,隨后出現的幾起案子,更是在濱河造成了極大的恐慌灶体,老刑警劉巖阅签,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異蝎抽,居然都是意外死亡政钟,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門樟结,熙熙樓的掌柜王于貴愁眉苦臉地迎上來养交,“玉大人,你說我怎么就攤上這事瓢宦∷榱” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵驮履,是天一觀的道長鱼辙。 經常有香客問我,道長玫镐,這世上最難降的妖魔是什么倒戏? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮恐似,結果婚禮上杜跷,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好葱椭,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布捂寿。 她就那樣靜靜地躺著,像睡著了一般孵运。 火紅的嫁衣襯著肌膚如雪秦陋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天治笨,我揣著相機與錄音驳概,去河邊找鬼。 笑死旷赖,一個胖子當著我的面吹牛顺又,可吹牛的內容都是我干的。 我是一名探鬼主播等孵,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼稚照,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了俯萌?” 一聲冷哼從身側響起果录,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咐熙,沒想到半個月后弱恒,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡棋恼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年返弹,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爪飘。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡义起,死狀恐怖,靈堂內的尸體忽然破棺而出悦施,到底是詐尸還是另有隱情并扇,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布抡诞,位于F島的核電站穷蛹,受9級特大地震影響,放射性物質發(fā)生泄漏昼汗。R本人自食惡果不足惜肴熏,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顷窒。 院中可真熱鬧蛙吏,春花似錦源哩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泼诱,卻和暖如春坛掠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背治筒。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工屉栓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人耸袜。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓友多,卻偏偏與公主長得像,于是被迫代替她去往敵國和親堤框。 傳聞我的和親對象是個殘疾皇子域滥,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容