Java 大白話講解設(shè)計(jì)模式之 -- 建造者(Builder)模式

聲明:原創(chuàng)作品烂琴,轉(zhuǎn)載請(qǐng)注明出處http://www.reibang.com/p/afe090b2e19c

建造者模式是日常開發(fā)中比較常見的設(shè)計(jì)模式披坏,它的主要作用就是將復(fù)雜事物創(chuàng)建的過程抽象出來句灌,該抽象的不同實(shí)現(xiàn)方式不同贰逾,創(chuàng)建出的對(duì)象也不同欣硼。通俗的講锅棕,創(chuàng)建一個(gè)對(duì)象一般都會(huì)有一個(gè)固定的步驟,這個(gè)固定的步驟我們把它抽象出來荆烈,每個(gè)抽象步驟都會(huì)有不同的實(shí)現(xiàn)方式拯勉,不同的實(shí)現(xiàn)方式創(chuàng)建出的對(duì)象也將不同。舉個(gè)常見的例子憔购,想必大家都買過電腦宫峦,電腦的生產(chǎn)或者組裝其實(shí)就是屬于建造者模式,我們知道玫鸟,電腦的生產(chǎn)都需要安裝CPU导绷、內(nèi)存條、硬盤等元器件鞋邑。我們可以把這個(gè)安裝步驟抽象出來诵次,至于到底裝哪種CPU账蓉,比如i5還是i7就是對(duì)該抽象安裝步驟的具體實(shí)現(xiàn)枚碗。

建造者模式分為兩種,一種為經(jīng)典建造者模式铸本,另一種為變種建造者模式肮雨。我們來挨個(gè)看下:

經(jīng)典Builder模式

我們先來看下經(jīng)典Builder模式,先上UML類圖:

Builder模式UML類圖

對(duì)UML類圖不了解可以看我之前的這篇文章Java 大白話講解設(shè)計(jì)模式之 -- UML類圖

從上圖可以看到箱玷,經(jīng)典Buider模式中有四個(gè)角色:

  1. 要建造的產(chǎn)品Product -- 組裝的電腦
  2. 抽象的Builder -- 裝CPU怨规、內(nèi)存條陌宿、硬盤等抽象的步驟
  3. Builder的具體實(shí)現(xiàn)ConcreteBuilder -- 對(duì)上述抽象步驟的實(shí)現(xiàn),比如裝i5CPU波丰、8G內(nèi)存條壳坪、1T硬盤
  4. 使用者Director -- 電腦裝機(jī)人員

接下來我們來看下用代碼如何實(shí)現(xiàn)上述組裝電腦的過程:

1.首先我們先來創(chuàng)建一個(gè)Computer類:

public class Computer {
    /*CPU*/
    private String CPU;
    /*內(nèi)存*/
    private String memory;
    /*硬盤*/
    private String hardDisk;
    /*鍵盤*/
    private String keyboard;
    /*鼠標(biāo)*/
    private String mouse;

    public String getCPU() {
        return CPU;
    }

    public void setCPU(String CPU) {
        this.CPU = CPU;
    }

    public String getMemory() {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(String hardDisk) {
        this.hardDisk = hardDisk;
    }

    public String getKeyboard() {
        return keyboard;
    }

    public void setKeyboard(String keyboard) {
        this.keyboard = keyboard;
    }

    public String getMouse() {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "CPU='" + CPU + '\'' +
                ", memory='" + memory + '\'' +
                ", hardDisk='" + hardDisk + '\'' +
                ", keyboard='" + keyboard + '\'' +
                ", mouse='" + mouse + '\'' +
                '}';
    }
}

很簡(jiǎn)單,可以看到這個(gè)Computer類中有五個(gè)基本屬性CPU掰烟、內(nèi)存條爽蝴、硬盤、鍵盤和鼠標(biāo)纫骑,然后還有一個(gè)toString方法蝎亚,用于之后方便打印信息用。

2.接下來我們來創(chuàng)建一個(gè)抽象的電腦組裝過程的Builder類:

public interface ComputerConfigBuilder {
    void setCPU();
    void setMemery();
    void setHardDisk();
    void setKeyboard();
    void setMouse();
    Computer getComputer();
}

電腦組裝一般都需要安裝CPU先馆、內(nèi)存條发框、硬盤、鍵盤鼠標(biāo)等煤墙,我們把這一安裝過程給抽象出來梅惯,也就是這里的ComputerConfigBuilder ,至于具體安裝什么需要其實(shí)現(xiàn)類來實(shí)現(xiàn)仿野,另外其中還定義了一個(gè)獲取Conputer的方法个唧。

3.好了,有了抽象的組裝過程设预,接下來我們就需要?jiǎng)?chuàng)建具體的實(shí)現(xiàn)類徙歼。我們知道電腦一般都有低配版和高配版,不同配置鳖枕,組裝成的電腦自然就不一樣魄梯。接下我們首先來創(chuàng)建一個(gè)低配版的套餐LowConfigBuilder ,讓其實(shí)現(xiàn)ComputerConfigBuilder:

public class LowConfigBuilder implements ComputerConfigBuilder {

    private Computer mComputer;

    public LowConfigBuilder(){
        this.mComputer = new Computer();
    }

    @Override
    public void setCPU() {
        mComputer.setCPU("i5");
    }

    @Override
    public void setMemery() {
        mComputer.setMemory("8G");
    }

    @Override
    public void setHardDisk() {
        mComputer.setHardDisk("500G");
    }

    @Override
    public void setKeyboard() {
        mComputer.setKeyboard("薄膜鍵盤");
    }

    @Override
    public void setMouse() {
        mComputer.setMouse("有線鼠標(biāo)");
    }

    @Override
    public Computer getComputer() {
        return mComputer;
    }
}

可以看到這個(gè)低配版的配置為:i5的CPU宾符、8G內(nèi)存酿秸、500G硬盤、薄膜鍵盤和有線鼠標(biāo)魏烫。

接著我們?cè)賱?chuàng)建一個(gè)高配版的套餐:

public class HighConfigBuider implements ComputerConfigBuilder {

    private Computer mComputer;

    public HighConfigBuider(){
        this.mComputer = new Computer();
    }

    @Override
    public void setCPU() {
        mComputer.setCPU("i7");
    }

    @Override
    public void setMemery() {
        mComputer.setMemory("16G");
    }

    @Override
    public void setHardDisk() {
        mComputer.setHardDisk("1T");
    }

    @Override
    public void setKeyboard() {
        mComputer.setKeyboard("機(jī)械鍵盤");
    }

    @Override
    public void setMouse() {
        mComputer.setMouse("無線鼠標(biāo)");
    }

    @Override
    public Computer getComputer() {
        return mComputer;
    }
}

可以看到這個(gè)高配版的配置為:i7的CPU辣苏、16G內(nèi)存、1T硬盤哄褒、機(jī)械鍵盤和無線鼠標(biāo)稀蟋。

4.上面我們已經(jīng)定義好了兩種配置方案,接下我們還需要一名裝機(jī)人員Director:

public class Director {
    private ComputerConfigBuilder mBuilder;
    public void setBuilder(ComputerConfigBuilder builder){
        this.mBuilder = builder;
    }
    public void createComputer(){
        mBuilder.setCPU();
        mBuilder.setMemery();
        mBuilder.setHardDisk();
        mBuilder.setKeyboard();
        mBuilder.setMouse();
    }
    public Computer getComputer(){
        return mBuilder.getComputer();
    }
}

我們需要通過setBuilder來告訴他電腦需要什么配置呐赡,然后就可以通過createComputer來一步步組裝電腦退客,組裝完之后就可以調(diào)用getComputer方法來獲取我們需要的電腦啦。
5.演示
接下來我們就來創(chuàng)建一臺(tái)電腦試下,首先我們先創(chuàng)建一個(gè)
低配版的:

Director director = new Director();//創(chuàng)建裝機(jī)人員
director.setBuilder(new LowConfigBuilder()); //告訴裝機(jī)人員電腦配置萌狂,這里為低配版
director.createComputer(); //裝機(jī)人員開始組裝
Computer computer = director.getComputer(); //從裝機(jī)人員獲取組裝好的電腦
System.out.print("電腦配置:" + computer.toString());  //查看電腦配置
--------------------------------------
輸出結(jié)果:
電腦配置:Computer{CPU='i5', memory='8G', hardDisk='500G', keyboard='薄膜鍵盤', mouse='有線鼠標(biāo)'}
--------------------------------------

高配版的:

director.setBuilder(new HighConfigBuider());
director.createComputer();
Computer computer = director.getComputer();
System.out.print("電腦配置:" + computer.toString());
--------------------------------------
輸出結(jié)果:
電腦配置:Computer{CPU='i7', memory='16G', hardDisk='1T', keyboard='機(jī)械鍵盤', mouse='無線鼠標(biāo)'}
--------------------------------------

好了這就是經(jīng)典的Builder模式档玻,到這里相信你對(duì)文章開頭Builder模式的定義有了更好的理解。不過在我們?nèi)粘i_發(fā)中經(jīng)典Builder模式一般不常用茫藏,用的比較多的還是變種的Builder模式误趴,接下來我們來看下變種的Builder模式。

變種Builder模式

今天Boss突然跑過來扔了一個(gè)需求給你:需要?jiǎng)?chuàng)建一個(gè)不可變的Person對(duì)象务傲,這個(gè)Person可以擁有以下幾個(gè)屬性:名字冤留、性別、年齡树灶、職業(yè)纤怒、車、鞋子天通、衣服泊窘、錢、房子像寒。其中名字和性別是必須有的烘豹。

聽完后,你稍微想了下诺祸,很快給出一個(gè)符合要求的Person類:

public class Person {
    /*名字(必須)*/
    private final String name;
    /*性別(必須)*/
    private final String gender;
    /*年齡(非必須)*/
    private final String age;
    /*鞋子(非必須)*/
    private final String shoes;
    /*衣服(非必須)*/
    private final String clothes;
    /*錢(非必須)*/
    private final String money;
    /*房子(非必須)*/
    private final String house;
    /*汽車(非必須)*/
    private final String car;
    /*職業(yè)(非必須)*/
    private final String career;

    public Person(String name,String gender,String age,String shoes,String clothes,String money,String house,String car,String career){
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.shoes = shoes;
        this.clothes = clothes;
        this.money = money;
        this.house = house;
        this.car = car;
        this.career = career;
    }

    public Person(String name, String gender){
        this(name,gender,null,null,null,null,null,null,null);
    }

}

由于要?jiǎng)?chuàng)建出的Person對(duì)象是不可變的携悯,所以你將類中的屬性都聲明為final的,然后定義了一個(gè)參數(shù)為所有屬性的構(gòu)造方法筷笨,又因?yàn)閚ame和gender為必須項(xiàng)憔鬼,所以你為了調(diào)用者方便又單獨(dú)定義了一個(gè)參數(shù)為name和gender的構(gòu)造方法。這樣Person類就好了胃夏,你信心滿滿的把這個(gè)類提交給了Boss,Boss看了轴或,還是很滿意的,不過一段時(shí)間后仰禀,Boss向你反饋了一個(gè)問題照雁,就是如果需要傳入非必須屬性的時(shí)候,這個(gè)構(gòu)造方法調(diào)用起來不是很方便答恶,因?yàn)檫@個(gè)構(gòu)造方法參數(shù)太多了饺蚊,很容易傳錯(cuò)。你試了下悬嗓,發(fā)現(xiàn)確實(shí)有這個(gè)問題污呼,看來不能把參數(shù)全都都放在構(gòu)造方法中,很快你想到了用set方法設(shè)置:

public class Person {
    /*名字(必須)*/
    private String name;
    /*性別(必須)*/
    private String gender;
    /*年齡(非必須)*/
    private String age;
    /*鞋子(非必須)*/
    private String shoes;
    /*衣服(非必須)*/
    private String clothes;
    /*錢(非必須)*/
    private String money;
    /*房子(非必須)*/
    private String house;
    /*汽車(非必須)*/
    private String car;
    /*職業(yè)(非必須)*/
    private String career;

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

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

    public String getAge() {
        return age;
    }

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

    public String getShoes() {
        return shoes;
    }

    public void setShoes(String shoes) {
        this.shoes = shoes;
    }

    ......

}

如果要?jiǎng)?chuàng)建對(duì)象的話只用如下操作就行了:

        Person person = new Person();
        person.setName("張三");
        person.setAge("22");
        person.setGender("男");
        person.setCareer("程序員");
        ......

這樣看上去比較清晰了烫扼,只要?jiǎng)?chuàng)建一個(gè)對(duì)象曙求,想要賦什么值set上去就可以了,不過你細(xì)細(xì)看了下映企,還是發(fā)現(xiàn)了不少的問題的悟狱,首先用這個(gè)set方法,違背了剛開始這個(gè)對(duì)象不可變的需求堰氓,其次用這種set方法一條一條賦值挤渐,逼格不夠高,另外用這種方式很可能會(huì)得到一個(gè)不完整的Person對(duì)象双絮,因?yàn)楫?dāng)你建完了Person對(duì)象浴麻,可能出于各方面的原因有些信息忘記set了,那么你得到的Person對(duì)象就不是你預(yù)期的對(duì)象囤攀。這時(shí)软免,你有點(diǎn)困惑了,只好打開谷歌一頓搜索焚挠,沒想到還真的找到了解決辦法膏萧,就是用下面這種變種的Builder模式:

public class Person {
    /*名字(必須)*/
    private final String name;
    /*性別(必須)*/
    private final String gender;
    /*年齡(非必須)*/
    private final String age;
    /*鞋子(非必須)*/
    private final String shoes;
    /*衣服(非必須)*/
    private final String clothes;
    /*錢(非必須)*/
    private final String money;
    /*房子(非必須)*/
    private final String house;
    /*汽車(非必須)*/
    private final String car;
    /*職業(yè)(非必須)*/
    private final String career;


    private Person(Builder builder) {
        this.name = builder.name;
        this.gender = builder.gender;
        this.age = builder.age;
        this.shoes = builder.shoes;
        this.clothes = builder.clothes;
        this.money = builder.money;
        this.house = builder.house;
        this.car = builder.car;
        this.career = builder.career;
    }

    public static class Builder {
        private final String name;
        private final String gender;
        private String age;
        private String shoes;
        private String clothes;
        private String money;
        private String house;
        private String car;
        private String career;

        public Builder(String name,String gender) {
            this.name = name;
            this.gender = gender;
        }

        public Builder age(String age) {
            this.age = age;
            return this;
        }

        public Builder car(String car) {
            this.car = car;
            return this;
        }

        public Builder shoes(String shoes) {
            this.shoes = shoes;
            return this;
        }

        public Builder clothes(String clothes) {
            this.clothes = clothes;
            return this;
        }

        public Builder money(String money) {
            this.money = money;
            return this;
        }

        public Builder house(String house) {
            this.house = house;
            return this;
        }

        public Builder career(String career) {
            this.career = career;
            return this;
        }

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


由于這個(gè)Person對(duì)象是不可變的,所以毫無疑問我們給他的所有屬性都加了final修飾蝌衔,當(dāng)然如果沒有不可變的需求也是可以不加的榛泛,然后在Person類中定義一個(gè)內(nèi)部類Builder,這個(gè)Builder內(nèi)部類中的屬性要和Person中的相同噩斟,并且必須有的屬性要用final修飾曹锨,防止這些屬性沒有被賦值,其他非必須的屬性不能用final剃允,因?yàn)槿绻恿薴inal沛简,就必須對(duì)其進(jìn)行初始化,這樣這些非必須的屬性又變成必須的斥废。然后內(nèi)部類中定義了一個(gè)構(gòu)造方法覆享,傳入必須有的屬性。其他非必須的屬性都通過方法設(shè)置营袜,每個(gè)方法都返回Builder對(duì)象自身撒顿。最后定義了一個(gè)build方法,將Builder對(duì)象傳入Person的私有構(gòu)造方法荚板,最終返回一個(gè)對(duì)象凤壁。

接下來我們來看下Person的創(chuàng)建:

        Person person = new Person.Builder("張三","男")
                .age("12")
                .money("1000000")
                .car("寶馬")
                .build();

是不是看上去逼格瞬間提高了,非必須的屬性可以根據(jù)需要任意設(shè)置跪另,非常靈活拧抖,而且這樣先設(shè)置屬性再創(chuàng)建對(duì)象,最終獲取的對(duì)象一定是你預(yù)期的完整對(duì)象免绿,不會(huì)像用之前set的方法創(chuàng)建的對(duì)象可能還沒有設(shè)置完全唧席。好了,寫完之后,你迫不及待的把這個(gè)Person類提交給了Boss淌哟,果然Boss對(duì)這種對(duì)象創(chuàng)建方式非常滿意迹卢。

好了兩種建造者模式到這也分析的差不多了,相信你對(duì)建造者模式有了更好的理解徒仓。

設(shè)計(jì)模式持續(xù)更新中...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腐碱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子掉弛,更是在濱河造成了極大的恐慌症见,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殃饿,死亡現(xiàn)場(chǎng)離奇詭異谋作,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乎芳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門遵蚜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秒咐,你說我怎么就攤上這事谬晕。” “怎么了携取?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵攒钳,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我雷滋,道長(zhǎng)不撑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任晤斩,我火速辦了婚禮焕檬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘澳泵。我一直安慰自己实愚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布兔辅。 她就那樣靜靜地躺著腊敲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪维苔。 梳的紋絲不亂的頭發(fā)上碰辅,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音介时,去河邊找鬼没宾。 笑死凌彬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的循衰。 我是一名探鬼主播铲敛,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼羹蚣!你這毒婦竟也來了原探?” 一聲冷哼從身側(cè)響起乱凿,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤顽素,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后徒蟆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胁出,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年段审,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了全蝶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寺枉,死狀恐怖抑淫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情姥闪,我是刑警寧澤始苇,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站筐喳,受9級(jí)特大地震影響催式,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜避归,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一荣月、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梳毙,春花似錦哺窄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牌废,卻和暖如春咽白,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸟缕。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工晶框, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留排抬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓授段,卻偏偏與公主長(zhǎng)得像蹲蒲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子侵贵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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