設(shè)計模式之Builder模式

??Builder設(shè)計模式在代碼設(shè)計中很常見牙寞,譬如我們在閱讀大神們的源碼是扫步,一些初始化參數(shù)比較多構(gòu)造方法都采用了Builder模式不狮,譬如說Okhttp,Glide,Picasso

Builder模式長什么樣?

??舉個簡單的例子色冀,現(xiàn)在大多數(shù)Android應(yīng)用的開發(fā)網(wǎng)絡(luò)層大多數(shù)都在使用OkHttp,熟悉的人都知道,在使用Okhttp之前要進(jìn)行一些初始化工作挪略,譬如說超時時間历帚,緩存策略等等,下面是個簡單的初始化例子:

 public static OkHttpClient get() {
        if (okHttpClient == null) {
            synchronized (AppHttpClient.class) {
                if (okHttpClient == null) {
                    okHttpClient = new OkHttpClient.Builder()
                            //cookie保存
                            .cookieJar(new CookiesManager(new PersistentCookieStore(BaseApplication.getInstance())))
                            .sslSocketFactory(createSSLSocketFactory())
                            //支持SSL
                            .hostnameVerifier(new HostnameVerifier() {
                                                  @Override
                                                  public boolean verify(String hostname, SSLSession session) {
                                                      return true;
                                                  }
                                              }
                            )
                            //鏈接15S超時
                            .connectTimeout(15, TimeUnit.SECONDS)
                            //Read請求30S超時
                            .readTimeout(30, TimeUnit.SECONDS)
                            //Write請求30S超時
                            .writeTimeout(30, TimeUnit.SECONDS)
                            //增加Header
                            .addInterceptor(new HeaderInfoInterceptor())
                            //build
                            .build();
                }
            }
        }
        return okHttpClient;
    }

上面的鏈?zhǔn)秸{(diào)用方法進(jìn)行初始化使用的就是Builder設(shè)計模式杠娱,是不是覺得很方便挽牢?

使用場景

??Builder可以將復(fù)雜對象的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現(xiàn)方法可以構(gòu)造出不同表現(xiàn)(屬性)的對象摊求。這句話看起來很拗口禽拔,不好理解,下邊我們舉一個簡單的例子:

我們在實際開發(fā)過程當(dāng)中室叉,時常會遇到這樣一個情況睹栖,需要構(gòu)建一個復(fù)雜(屬性N多)的對象,像這樣嬸兒的:

public class Person {
    private String name;        //必須
    private int age;            //必須
    private int sex;            //必須
    private float height;
    private String phone;
    private String address;
    private String email;
}

想要new一個這樣的類的實例茧痕,于是我們就開始擼下邊的代碼野来,通過構(gòu)造函數(shù)的參數(shù)的方式去new一個對象:

public Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name, int age, float height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public Person(String name, int age, float height, int sex) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
    }

    public Person(String name, int age, float height, int sex, String phone) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
        this.phone = phone;
    }

    public Person(String name, int age, float height, int sex, String phone, String address) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
        this.phone = phone;
        this.address = address;
    }

    public Person(String name, int age, float height, int sex, String phone, String address, String email) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
        this.phone = phone;
        this.address = address;
        this.email = email;
    }

或者使用getter和setter的方式去寫一個實現(xiàn)類:

public class Person {
    private String name;        //必須
    private int age;            //必須
    private int sex;            //必須
    private float height;
    private String phone;
    private String address;
    private String email;

    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 getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

先說說這兩種方式的優(yōu)劣:

第一種在參數(shù)不多的情況下,是比較方便快捷的踪旷,一旦參數(shù)多了曼氛,代碼可讀性大大降低豁辉,并且難以維護(hù),對調(diào)用者來說也造成一定困惑舀患;

第二種可讀性不錯徽级,也易于維護(hù),但是這樣子做對象會產(chǎn)生不一致的狀態(tài)聊浅,當(dāng)你想要傳入全部參數(shù)的時候餐抢,你必需將所有的setXX方法調(diào)用完成之后才行。然而一部分的調(diào)用者看到了這個對象后狗超,以為這個對象已經(jīng)創(chuàng)建完畢,就直接使用了朴下,其實Person對象并沒有創(chuàng)建完成努咐,另外,這個Person對象也是可變的殴胧,不可變類所有好處都不復(fù)存在渗稍。

所以有沒有更好地方式去實現(xiàn)它呢,那就是接下來要理解的Builder模式了团滥。

以下介紹的Builder模式其實是Builder模式的衍生模式竿屹,他與最經(jīng)典Builder模式有區(qū)別,因為現(xiàn)在很少使用經(jīng)典Builder模式灸姊,基本都在使用Builder衍生模式(鏈?zhǔn)秸{(diào)用)拱燃,所以經(jīng)典Builder模式本人也沒怎么研究,有興趣的童鞋可以自行研究下設(shè)計模式之四 --- 建造(Builder)模式

新擼出來的代碼:

public class Person {
    private final String name;        //必須
    private final int age;            //必須
    private final int sex;            //必須
    private final float height;
    private final String phone;
    private final String address;
    private final String email;


    private Person(PersonBuilder builder){
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.height = builder.height;
        this.phone = builder.phone;
        this.address = builder.address;
        this.email = builder.email;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getSex() {
        return sex;
    }

    public float getHeight() {
        return height;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public String getEmail() {
        return email;
    }


    public static class PersonBuilder{
        private final String name;        //必須
        private final int age;            //必須
        private final int sex;            //必須
        private  float height;
        private  String phone;
        private  String address;
        private  String email;

        public PersonBuilder(String name, int age, int sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }

        public PersonBuilder height(float height){
            this.height = height;
            return this;
        }

        public PersonBuilder phone(String phone){
            this.phone = phone;
            return this;
        }

        public PersonBuilder address(String address){
            this.address = address;
            return this;
        }

        public PersonBuilder email(String email){
            this.email = email;
            return this;
        }

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

從上面的代碼中我們可以看到力惯,我們在PersonBuilder類里定義了一份與Person一模一樣的變量碗誉,通過一系列的成員函數(shù)進(jìn)行設(shè)置屬性值,但是返回值都是this父晶,也就是都是PersonBuilder對象哮缺,最后提供了一個build函數(shù)用于創(chuàng)建Person對象,返回的是Person對象甲喝,對應(yīng)的構(gòu)造函數(shù)在Person類中進(jìn)行定義尝苇,也就是構(gòu)造函數(shù)的入?yún)?/code>是PersonBuilder對象,然后依次對自己的成員變量進(jìn)行賦值埠胖,對應(yīng)的值都是PersonBuilder對象中的值糠溜。此外PersonBuilder類中的成員函數(shù)返回Builder對象自身的另一個作用就是讓它支持鏈?zhǔn)秸{(diào)用,使代碼可讀性大大增強(qiáng)直撤。

Builder模式需要有幾個注意的點:

  1. Person類的構(gòu)造方法必須是私有的诵冒,調(diào)用者不能直接創(chuàng)建Person對象
  2. Person類的屬性是不可變的,所有的屬性都要添加final修飾符谊惭,并且在構(gòu)造方法中設(shè)置了值汽馋,并且對外只提供getters方法侮东。
  3. PersonBuilder的內(nèi)部類構(gòu)造方法中只接收必傳的參數(shù),并且必傳的參數(shù)使用final修飾符豹芯。

調(diào)用方式

new Person.PersonBuilder("張三", 18, 1)
                .address("北京市XXX")
                .email("xxxxx@gmail.com")
                .height(175.5f)
                .phone("1234556")
                .build();

相比起前面通過構(gòu)造函數(shù)和setter/getter方法兩種方式,可讀性更強(qiáng)悄雅。唯一可能存在的問題就是會產(chǎn)生多余的Builder對象,消耗內(nèi)存铁蹈。然而大多數(shù)情況下我們的Builder內(nèi)部類使用的是靜態(tài)修飾的(static)宽闲,所以這個問題也沒多大關(guān)系。

怎么樣Builder設(shè)計模式會使你的代碼看上去高大上一些呢握牧?

Android Studio對Builder模式的支持

AS 對Builder這種常用的設(shè)計模式提供了支持容诬,方便我們的代碼編寫,在Plugins中搜索Builder沿腰,會出現(xiàn)一個InnerBuilder的插件览徒,

1.jpeg

在編寫好類和屬性之后,右鍵generate選擇Builder颂龙,選擇屬性

2.jpeg

確定之后就會生成Builder模式下的代碼习蓬,當(dāng)然生成的代碼和我們希望的還是有些不同,只需要進(jìn)行小小的修改就可以了措嵌,還是很方便的躲叼!

總結(jié)

優(yōu)點

  1. 良好的封裝性, 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)企巢;
  2. 建造者獨立枫慷,容易擴(kuò)展;

缺點

  1. 會產(chǎn)生多余的Builder對象浪规,消耗內(nèi)存流礁;
  2. 對象的構(gòu)建過程暴露。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罗丰,一起剝皮案震驚了整個濱河市神帅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萌抵,老刑警劉巖找御,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绍填,居然都是意外死亡霎桅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門讨永,熙熙樓的掌柜王于貴愁眉苦臉地迎上來滔驶,“玉大人,你說我怎么就攤上這事卿闹〗腋猓” “怎么了萝快?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長著角。 經(jīng)常有香客問我揪漩,道長,這世上最難降的妖魔是什么吏口? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任奄容,我火速辦了婚禮,結(jié)果婚禮上产徊,老公的妹妹穿的比我還像新娘昂勒。我一直安慰自己,他們只是感情好舟铜,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布戈盈。 她就那樣靜靜地躺著,像睡著了一般深滚。 火紅的嫁衣襯著肌膚如雪奕谭。 梳的紋絲不亂的頭發(fā)上涣觉,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天痴荐,我揣著相機(jī)與錄音,去河邊找鬼官册。 笑死生兆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的膝宁。 我是一名探鬼主播鸦难,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼员淫!你這毒婦竟也來了合蔽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤介返,失蹤者是張志新(化名)和其女友劉穎拴事,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體圣蝎,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡刃宵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了徘公。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牲证。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖关面,靈堂內(nèi)的尸體忽然破棺而出坦袍,到底是詐尸還是另有隱情十厢,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布键闺,位于F島的核電站寿烟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辛燥。R本人自食惡果不足惜筛武,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挎塌。 院中可真熱鬧徘六,春花似錦、人聲如沸榴都。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘴高。三九已至竿音,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拴驮,已是汗流浹背春瞬。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留套啤,地道東北人宽气。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像潜沦,于是被迫代替她去往敵國和親萄涯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容