Android設(shè)計模式之建造者模式(builder pattern)

原文:http://blog.csdn.net/nugongahou110 https://blog.csdn.net/nugongahou110/article/details/50395698
builder設(shè)計模式我們很常見,比如我們使用AlertDialog的時候就使用的builder設(shè)計模式,著名的Universal-Image-Loader的初始化配置也是使用的builder設(shè)計模式弄捕,那么他們?yōu)槭裁词褂胋uilder設(shè)計模式,什么情況下我們應(yīng)該考慮使用builder設(shè)計模式艰额,這是我們很多人的疑惑,即便是學(xué)會了也不知道什么時候應(yīng)該使用,這篇文章我將告訴大家在什么情況下應(yīng)該考慮使用builder設(shè)計模式买乃,如果不用的話會有什么壞處饲漾,用了會有什么好處蝙搔。

如果我們有一個類Student,他有很多的屬性考传,但是僅僅姓名和學(xué)號是必須賦值的吃型,其他的屬性都是可選項,比如像下面代碼中所示

public class Student {
    private final int stuId;//必須
    private final String name;//必須
    private final int age;//可選
    private final int gender;//可選
    private final int address;//可選
    ...//還有很多可選屬性
}

那么我們怎么來創(chuàng)建一個Student對象呢僚楞?我們看到每個屬性都用final來修飾了勤晚,說明每個屬性都要在構(gòu)造方法中被初始化,我們又必須提供各種參數(shù)數(shù)量的構(gòu)造方法泉褐,我們看如下代碼

public class Student {
    private final int stuId;//必須
    private final String name;//必須
    private final int age;//可選
    private final int gender;//可選
    private final String address;//可選

    public Student(int stuId,String name){
        this(stuId,name,0,1,"");
    }
    public Student(int stuId,String name,int age){
        this(stuId,name,age,1,"");
    }
    public Student(int stuId,String name,int age,int gender){
        this(stuId,name,age,gender,"");
    }
    public Student(int stuId,String name,int age,int gender,String address){
        this.stuId = stuId;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.address = address;
    }
}

這樣做確實可以解決我們的需求赐写,但這還是可選參數(shù)不多的情況,如果有很多可選參數(shù)兴枯,我們就必須要寫很多個構(gòu)造函數(shù)血淌,這將導(dǎo)致代碼的可讀性和維護性變差,更重要的是财剖,當(dāng)我們要用到這個類的時候會感覺無從下手悠夯,我到底應(yīng)該用哪個構(gòu)造方法呢?應(yīng)該用兩個參數(shù)的構(gòu)造方法還是用三個參數(shù)的呢躺坟?如果我用兩個參數(shù)的構(gòu)造方法沦补,那么可選參數(shù)的默認(rèn)值是多少?

更棘手的是咪橙,如果我只想給Student對象設(shè)置address屬性而不設(shè)置age和gender屬性的話怎么辦夕膀?我們顯然還得再繼續(xù)添加構(gòu)造方法,或者我們只能調(diào)用全參的構(gòu)造方法美侦,然后給age和gender屬性設(shè)置個默認(rèn)值产舞。

還有一點,我們看到stuId菠剩,age易猫,gender都是int類型的,那么我們在創(chuàng)建Student對象時具壮,哪一個int類型的對象代表stuId准颓,哪一個代表age哈蝇,這還進一步增加了使用成本。

那么我們還有沒有其他的辦法攘已?答案是有炮赦!我們可以只設(shè)置一個默認(rèn)的無參構(gòu)造方法,然后給每個屬性添加getter和setter方法样勃,代碼如下

public class Student {
    private int stuId;//必須
    private String name;//必須
    private int age;//可選
    private int gender;//可選
    private String address;//可選

    public Student(){

    }

    public int getStuId() {
        return stuId;
    }

    public void setStuId(int stuId) {
        this.stuId = stuId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getGender() {
        return gender;
    }

    public void setGender(int gender) {
        this.gender = gender;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

這種方法看上去可讀性和維護性比較好吠勘,當(dāng)我們使用這個類的時候只需要創(chuàng)建一個空的對象并且設(shè)置我們需要的屬性就可以了。比如這樣:

 Student stu = new Student();
 stu.setStuId(1);
 stu.setName("小明");
 stu.setAge(12);

這樣做有兩個問題峡眶,第一個問題是我們的stu對象沒有一個創(chuàng)建完畢的標(biāo)識看幼,上面的stu對象我們設(shè)置了三個屬性,但當(dāng)別人看到這段代碼時幌陕,他不確定這個stu對象是只需要這三個屬性還是當(dāng)時作者忘了寫完整,除非所有的屬性都給set上汽煮,別人才能確保你這個對象創(chuàng)建完畢搏熄;另一個問題是任何人都可以在我們創(chuàng)建好的基礎(chǔ)上繼續(xù)改變它,也就是繼續(xù)給它set新的屬性或者刪除某個已經(jīng)set的屬性暇赤,這就會使我們的stu對象具有可變性心例,這會引起潛在的風(fēng)險。

好在我們還有第三種方法鞋囊,那就是builder設(shè)計模式了止后。

public class Student {
    private final int stuId;//必須
    private final String name;//必須
    private final int age;//可選
    private final int gender;//可選
    private final String address;//可選

    private Student(StudentBuilder builder){
        this.stuId = builder.stuId;
        this.name = builder.name;
        this.age = builder.age;
        this.gender = builder.gender;
        this.address = builder.address;
    }

    public int getStuId() {
        return stuId;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getGender() {
        return gender;
    }

    public String getAddress() {
        return address;
    }

    public static class StudentBuilder{
        private final int stuId;
        private final String name;
        private int age;
        private int gender;
        private String address;

        public StudentBuilder(int stuId,String name){
            this.stuId = stuId;
            this.name = name;
        }
        public StudentBuilder setAge(int age){
            this.age = age;
            return this;
        }
        public StudentBuilder setGender(int gender){
            this.gender = gender;
            return this;
        }
        public StudentBuilder setAddress(String address){
            this.address = address;
            return this;
        }
        public Student build(){
            return new Student(this);
        }
    }

}

值得注意的幾點:
1.Student的構(gòu)造方法是私有的,也就是說我們不能直接new出Student對象
2.我們又將Student的屬性用final修飾了溜腐,并且我們在構(gòu)造方法中都為他們進行了初始化操作译株,我們只提供了getter方法
3.使用builder模式構(gòu)造出來的對象有更好的可讀性,等下我們會看到
4.StudentBuilder的屬性中只給我們必須的屬性添加的final修飾挺益,所以我們必須在StudentBuilder的構(gòu)造方法中為他們初始化

使用builder設(shè)計模式完美的解決了方法一和方法二的不足歉糜,并且兼具他們的優(yōu)點:具有必填屬性和可選屬性的區(qū)分,更重要的是:可讀性很強望众。唯一的不足是我們要在StudentBuilder中重復(fù)的寫一遍Student中的屬性匪补。

好,現(xiàn)在我們來創(chuàng)建一個Student對象吧

public Student getStudent(){
        return new Student.StudentBuilder(1,"小明")//必填屬性在構(gòu)造方法中賦值
                    .setAge(1)//設(shè)置可選屬性 年齡
                    .setGender(1)//設(shè)置可選屬性 性別 默認(rèn)1為男
                    .build();//對象構(gòu)建完畢的標(biāo)識烂翰,返回Student對象
    }

非常優(yōu)雅有木有夯缺?他是一個鏈?zhǔn)降恼{(diào)用,我們可以1行代碼就搞定甘耿,更重要的是踊兜,他的可讀性非常強,而且通過build()我們可以很明確的告訴別人我們的Student已經(jīng)創(chuàng)建完畢棵里。

builder設(shè)計模式非常靈活润文,一個builder可以創(chuàng)建出各種各樣的對象姐呐,我們只需要在build()之前調(diào)用set方法來為我們的對象賦值。

builder模式另一個重要特性是:它可以對參數(shù)進行合法性驗證典蝌,如果我們傳入的參數(shù)無效曙砂,我們可以拋出一個IllegalStateException異常,但是我們在哪里進行參數(shù)合法性驗證也是有講究的:那就是在對象創(chuàng)建之后進行合法性驗證骏掀。我們修改StudentBuilder的build()方法

 public Student build(){
            Student student = new Student(this);
            if (student.getAge()>120){
                throw  new IllegalStateException("年齡超出限制");
            }
            return student;
        }

為什么要先創(chuàng)建對象鸠澈,再進行參數(shù)驗證?因為我們的StudentBuilder是線程不安全的截驮,如果我們先進行參數(shù)驗證后創(chuàng)建對象笑陈,那么創(chuàng)建對象的時候?qū)ο蟮膶傩钥赡芤呀?jīng)被其他線程改變了,例如下面的代碼就是錯誤的

  /**
         * 錯誤的
         */
        public Student build(){
            if (age>120){
                throw  new IllegalStateException("年齡超出限制");
            }
            return new Student(this);
        }

最后總結(jié)一下builder設(shè)計模式:當(dāng)我們的類中有很多屬性的時候葵袭,更重要的是有很多可選屬性的時候涵妥,我們就可以使用builder設(shè)計模式,因為這樣不僅可以使我們的類使用起來很優(yōu)雅坡锡,而且還可以給我們的對象一個創(chuàng)建完成的標(biāo)識蓬网,即build()方法。

作者:阿拉燈神燈
來源:CSDN
原文:https://blog.csdn.net/nugongahou110/article/details/50395698
版權(quán)聲明:本文為博主原創(chuàng)文章鹉勒,轉(zhuǎn)載請附上博文鏈接帆锋!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市禽额,隨后出現(xiàn)的幾起案子锯厢,更是在濱河造成了極大的恐慌,老刑警劉巖脯倒,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件实辑,死亡現(xiàn)場離奇詭異,居然都是意外死亡盔憨,警方通過查閱死者的電腦和手機徙菠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來郁岩,“玉大人婿奔,你說我怎么就攤上這事∥噬鳎” “怎么了萍摊?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長如叼。 經(jīng)常有香客問我冰木,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任踊沸,我火速辦了婚禮歇终,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逼龟。我一直安慰自己评凝,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布腺律。 她就那樣靜靜地躺著奕短,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匀钧。 梳的紋絲不亂的頭發(fā)上翎碑,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音之斯,去河邊找鬼日杈。 笑死,一個胖子當(dāng)著我的面吹牛佑刷,可吹牛的內(nèi)容都是我干的达椰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼项乒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了梁沧?” 一聲冷哼從身側(cè)響起檀何,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎廷支,沒想到半個月后频鉴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡恋拍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年垛孔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片施敢。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡周荐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出僵娃,到底是詐尸還是另有隱情概作,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布默怨,位于F島的核電站讯榕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愚屁,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一济竹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧霎槐,春花似錦送浊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碍岔,卻和暖如春浴讯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔼啦。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工榆纽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捏肢。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓奈籽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸵赫。 傳聞我的和親對象是個殘疾皇子衣屏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355