一篇文章就徹底弄懂建造者模式(Builder Pattern)

背景

當(dāng)一個(gè)類(lèi)的內(nèi)部數(shù)據(jù)過(guò)于復(fù)雜的時(shí)候(通常是負(fù)責(zé)持有數(shù)據(jù)的類(lèi)霸奕,比如Config溜宽、VO、PO质帅、Entity...)适揉,要?jiǎng)?chuàng)建的話(huà)可能就需要了解這個(gè)類(lèi)的內(nèi)部結(jié)構(gòu),還有這些東西是怎么組織裝配等一大坨亂七八糟的東西煤惩,這個(gè)時(shí)候就會(huì)增加學(xué)習(xí)成本而且會(huì)很混亂嫉嘀,這個(gè)時(shí)候就想啊想一種什么法子來(lái)管理一下這個(gè)類(lèi)中的數(shù)據(jù)呢,怎么在創(chuàng)建的時(shí)候讓它按部就班的來(lái)魄揉,并且代碼可讀性很好別讓我看花了眼啊剪侮,我要的東西也能都很好設(shè)置進(jìn)來(lái),這就是Builder模式的應(yīng)用場(chǎng)景什猖,Builder模式可以將一個(gè)類(lèi)的構(gòu)建和表示進(jìn)行分離票彪。

1.介紹

1.1什么是構(gòu)建者模式

創(chuàng)建者模式又叫建造者模式,是將一個(gè)復(fù)雜的對(duì)象的構(gòu)建與它的表示分離不狮,使
得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示降铸。創(chuàng)建者模式隱藏了復(fù)雜對(duì)象的創(chuàng)建過(guò)程,它把復(fù)雜對(duì)象的創(chuàng)建過(guò)程加以抽象摇零,通過(guò)子類(lèi)繼承或者重載的方式推掸,動(dòng)態(tài)的創(chuàng)建具有復(fù)合屬性的對(duì)象。

1.2適用場(chǎng)景:

  • 隔離復(fù)雜對(duì)象的創(chuàng)建和使用驻仅,相同的方法谅畅,不同執(zhí)行順序,產(chǎn)生不同事件結(jié)果
  • 多個(gè)部件都可以裝配到一個(gè)對(duì)象中噪服,但產(chǎn)生的運(yùn)行結(jié)果不相同
  • 產(chǎn)品類(lèi)非常復(fù)雜或者產(chǎn)品類(lèi)因?yàn)檎{(diào)用順序不同而產(chǎn)生不同作用
  • 初始化一個(gè)對(duì)象時(shí)毡泻,參數(shù)過(guò)多,或者很多參數(shù)具有默認(rèn)值
  • Builder模式不適合創(chuàng)建差異性很大的產(chǎn)品類(lèi)
    產(chǎn)品內(nèi)部變化復(fù)雜粘优,會(huì)導(dǎo)致需要定義很多具體建造者類(lèi)實(shí)現(xiàn)變化仇味,增加項(xiàng)目中類(lèi)的數(shù)量,增加系統(tǒng)的理解難度和運(yùn)行成本
  • 需要生成的產(chǎn)品對(duì)象有復(fù)雜的內(nèi)部結(jié)構(gòu)雹顺,這些產(chǎn)品對(duì)象具備共性丹墨;

1.3 主要作用

在用戶(hù)不知道對(duì)象的建造過(guò)程和細(xì)節(jié)的情況下就可以直接創(chuàng)建復(fù)雜的對(duì)象。

  • 用戶(hù)只需要給出指定復(fù)雜對(duì)象的類(lèi)型和內(nèi)容嬉愧;
  • 建造者模式負(fù)責(zé)按順序創(chuàng)建復(fù)雜對(duì)象(把內(nèi)部的建造過(guò)程和細(xì)節(jié)隱藏起來(lái))

1.4 解決的問(wèn)題

  • 方便用戶(hù)創(chuàng)建復(fù)雜的對(duì)象(不需要知道實(shí)現(xiàn)過(guò)程)
  • 代碼復(fù)用性 & 封裝性(將對(duì)象構(gòu)建過(guò)程和細(xì)節(jié)進(jìn)行封裝 & 復(fù)用)

例子:造汽車(chē) & 買(mǎi)汽車(chē)贩挣。

  1. 工廠(建造者模式):負(fù)責(zé)制造汽車(chē)(組裝過(guò)>程和細(xì)節(jié)在工廠內(nèi))
  2. 汽車(chē)購(gòu)買(mǎi)者(用戶(hù)):你只需要說(shuō)出你需要的>型號(hào)(對(duì)象的類(lèi)型和內(nèi)容),然后直接購(gòu)買(mǎi)就可>>以使用了
    (不需要知道汽車(chē)是怎么組裝的(車(chē)輪、車(chē)門(mén)王财、>發(fā)動(dòng)機(jī)卵迂、方向盤(pán)等等))

2. 模式原理

2.1 UML類(lèi)圖 & 組成

builder.png

2.2模式講解:

  • 指揮者(Director)直接和客戶(hù)(Client)進(jìn)行需求溝通;
  • 溝通后指揮者將客戶(hù)創(chuàng)建產(chǎn)品的需求劃分為各個(gè)部件的建造請(qǐng)求(Builder)搪搏;
  • 將各個(gè)部件的建造請(qǐng)求委派到具體的建造者(ConcreteBuilder)狭握;
  • 各個(gè)具體建造者負(fù)責(zé)進(jìn)行產(chǎn)品部件的構(gòu)建;
  • 最終構(gòu)建成具體產(chǎn)品(Product)疯溺。

3.用 builder 模式創(chuàng)建共享單車(chē)為例子论颅,示例代碼:

產(chǎn)品類(lèi):


public class Bike { 

    private IFrame frame; 
    private ISeat seat; 
    private ITire tire; 
    
    public IFrame getFrame() { 
        return frame; 
    } 
    public void setFrame(IFrame frame) { 
        this.frame = frame; 
    } 
    public ISeat getSeat() { 
        return seat; 
    } 
    public void setSeat(ISeat seat) { 
        this.seat = seat; 
    } 
    public ITire getTire() { 
        return tire; 
    } 
    public void setTire(ITire tire) { 
        this.tire = tire; 
    } 
} 

Builder 類(lèi):

// 抽象 builder 類(lèi) 
public abstract class Builder { 
    abstract void buildFrame(); 
    abstract void buildSeat(); 
    abstract void buildTire(); 
    abstract Bike createBike(); 
} 

ConcreteBuilder 類(lèi) :

// 具體 builder 類(lèi) 
public class MobikeBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new AlloyFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new DermisSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new SolidTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
} 

public class OfoBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new CarbonFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new RubberSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new InflateTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
} 

指揮者類(lèi):

public class Director { 
    private Builder mBuilder = null; 
    public Director(Builder builder) { 
        mBuilder = builder; 
    } 
    public Bike construct() { 
        mBuilder.buildFrame(); 
        mBuilder.buildSeat(); 
        mBuilder.buildTire(); 
        return mBuilder.createBike(); 
    } 
}

客戶(hù)端使用:

public class Click { 
    public static void main(String[] args) { 
        showBike(new OfoBuilder()); 
        showBike(new MobikeBuilder()); 
    } 
    private void showBike(Builder builder) {
        Director director = new Director(builder); 
        Bike bike = director.construct(); 
        bike.getFrame().frame(); 
        bike.getSeat().seat(); 
        bike.getTire().tire(); 
    } 
} 

上面示例是 Builder模式的常規(guī)用法,導(dǎo)演類(lèi) Director 在 Builder模式中具有很重要的作用囱嫩,它用于指導(dǎo)具體構(gòu)建者如何構(gòu)建產(chǎn)品恃疯,控制調(diào)用先后次序,并向調(diào)用者返回完整的產(chǎn)品類(lèi)墨闲,但是有些情況下需要簡(jiǎn)化系統(tǒng)結(jié)構(gòu)今妄,可以把Director和抽象建造者進(jìn)行結(jié)合,示例代碼:

改造后的抽象建造者:

public abstract class NewBuilder { 
    abstract void buildFrame(); 
    abstract void buildSeat(); 
    abstract void buildTire(); 
    abstract Bike createBike(); 
    /** 
    * 把導(dǎo)演類(lèi)中的construct()方法合并到抽象建造者類(lèi)中 
    * 
    * @return 具體產(chǎn)品對(duì)象 
    */ 
    public Bike construct() { 
        this.buildFrame(); 
        this.buildSeat(); 
        this.buildTire(); 
        return this.createBike(); 
    } 
} 

這樣做確實(shí)簡(jiǎn)化了系統(tǒng)結(jié)構(gòu)鸳碧,但同時(shí)也加重了抽象建造者類(lèi)的職責(zé)盾鳞,也不是太符合單一職責(zé)原則,如果construct() 過(guò)于復(fù)雜瞻离,建議還是封裝到 Director 中
除了上面的用途外腾仅,還有另外一個(gè)常用的使用方式,就是當(dāng)一個(gè)類(lèi)構(gòu)造器需要傳入很多參數(shù)時(shí)套利,如果創(chuàng)建這個(gè)類(lèi)的實(shí)例推励,代碼可讀性會(huì)非常差,而且很容易引入錯(cuò)誤肉迫,此時(shí)就可以利用 builder模式進(jìn)行重構(gòu)验辞,重構(gòu)前示例代碼:

// 省略 getter 和 setter 方法 
public class Computer { 
    private String cpu; 
    private String screen; 
    private String memory; 
    private String mainboard; 
    public Computer(String cpu, String screen, String memory, String mainboard) { 
        this.cpu = cpu; 
        this.screen = screen; 
        this.memory = memory; 
        this.mainboard = mainboard; 
    } 
} 
public class NewComputer { 
    private String cpu; 
    private String screen; 
    private String memory; 
    private String mainboard; 
    public NewComputer() { 
        throw new RuntimeException(“can’t init”); 
    } 
    private NewComputer(Builder builder) { 
        cpu = builder.cpu; 
        screen = builder.screen; 
        memory = builder.memory; 
        mainboard = builder.mainboard; 
    } 
    public static final class Builder { 
        private String cpu; 
        private String screen; 
        private String memory; 
        private String mainboard; 
        
    public Builder() {} 
    
    public Builder cpu(String val) { 
        cpu = val; 
        return this; 
    } 
    public Builder screen(String val) { 
        screen = val; 
        return this; 
    } 
    public Builder memory(String val) { 
        memory = val; 
        return this; 
    } 
    public Builder mainboard(String val) { 
        mainboard = val; 
        return this; 
    } 
    public NewComputer build() {
        return new  NewComputer(this);} 
    } 
} 

客戶(hù)端:


public class Click { 
    public static void main(String[] args) { 
        // 非 Builder 模式 
        Computer computer = new Computer(“cpu”, “screen”, “memory”, “mainboard”); 
        // Builder 模式 
        NewComputer newComputer = new NewComputer.Builder() 
        .cpu(“cpu”) 
        .screen(“screen”) 
        .memory(“memory”) 
        .mainboard(“mainboard”) 
        .build(); 
    } 
} 

上面的示例代碼只是傳入四個(gè)參數(shù),如果參數(shù)是十四個(gè)甚至更多喊衫,builder 模式的優(yōu)勢(shì)將會(huì)更加明顯跌造,傳遞參數(shù)更加靈活,代碼具有更高的可讀性.

4.優(yōu)缺點(diǎn)比較

一般的套路:優(yōu)點(diǎn)是比較簡(jiǎn)單族购,開(kāi)發(fā)效率高鼻听,缺點(diǎn)是如果參數(shù)真的很多的話(huà)鬼知道每個(gè)對(duì)應(yīng)的是什么意思啊。

Builder模式:優(yōu)點(diǎn)是可以將構(gòu)造器的setter方法名取成類(lèi)似注釋的方式联四,這樣我們可以很清晰的知道剛才究竟設(shè)置的什么值,可讀性較高撑教,缺點(diǎn)是比較冗長(zhǎng)朝墩。

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

  • 使用建造者模式可以使客戶(hù)端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。
  • 具體的建造者類(lèi)之間是相互獨(dú)立的,這有利于系統(tǒng)的擴(kuò)展收苏。
  • 具體的建造者相互獨(dú)立亿卤,因此可以對(duì)建造的過(guò)程逐步細(xì)化,而不會(huì)對(duì)其他模塊產(chǎn)生任何影響鹿霸。

缺點(diǎn):

  • 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn)排吴,其組成部分相似;如果產(chǎn)品之間的差異性很大懦鼠,則不適合使用建造者模式钻哩,因此其使用范圍受到一定的限制。
  • 如果產(chǎn)品的內(nèi)部變化復(fù)雜肛冶,可能會(huì)導(dǎo)致需要定義很多具體建造者類(lèi)來(lái)實(shí)現(xiàn)這種變化街氢,導(dǎo)致系統(tǒng)變得很龐大。

5.建造者模式與抽象工廠模式的比較:

  • 與抽象工廠模式相比睦袖,建造者模式返回一個(gè)組裝好的完整產(chǎn)品珊肃,而抽象工廠模式返回一系列相關(guān)的產(chǎn)品,這些產(chǎn)品位于不同的產(chǎn)品等級(jí)結(jié)構(gòu)馅笙,構(gòu)成了一個(gè)產(chǎn)品族 伦乔。
  • 在抽象工廠模式中,客戶(hù)端實(shí)例化工廠類(lèi)董习,然后調(diào)用工廠方法獲取所需產(chǎn)品對(duì)象烈和,而在建造者模式中,客戶(hù)端可以不直接調(diào)用建造者的相關(guān)方法阱飘,而是通過(guò)指揮者類(lèi)來(lái)指導(dǎo)如何生成對(duì)象斥杜,包括對(duì)象的組裝過(guò)程和建造步驟,它側(cè)重于一步步構(gòu)造一個(gè)復(fù)雜對(duì)象沥匈,返回一個(gè)完整的對(duì)象 蔗喂。
  • 如果將抽象工廠模式看成汽車(chē)配件生產(chǎn)工廠,生產(chǎn)一個(gè)產(chǎn)品族的產(chǎn)品高帖,那么建造者模式就是一個(gè)汽車(chē)組裝工廠缰儿,通過(guò)對(duì)部件的組裝可以返回一輛完整的汽車(chē)

改文章已同步到公眾號(hào),歡迎大家關(guān)注散址!


大前端圈_small.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乖阵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子预麸,更是在濱河造成了極大的恐慌瞪浸,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吏祸,死亡現(xiàn)場(chǎng)離奇詭異对蒲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)蹈矮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)砰逻,“玉大人,你說(shuō)我怎么就攤上這事泛鸟◎鹋兀” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵北滥,是天一觀的道長(zhǎng)刚操。 經(jīng)常有香客問(wèn)我,道長(zhǎng)碑韵,這世上最難降的妖魔是什么赡茸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮祝闻,結(jié)果婚禮上占卧,老公的妹妹穿的比我還像新娘。我一直安慰自己联喘,他們只是感情好华蜒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著豁遭,像睡著了一般叭喜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蓖谢,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天捂蕴,我揣著相機(jī)與錄音,去河邊找鬼闪幽。 笑死啥辨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盯腌。 我是一名探鬼主播溉知,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腕够!你這毒婦竟也來(lái)了级乍?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤帚湘,失蹤者是張志新(化名)和其女友劉穎玫荣,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體大诸,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崇决,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年材诽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恒傻。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖建邓,靈堂內(nèi)的尸體忽然破棺而出盈厘,到底是詐尸還是另有隱情,我是刑警寧澤官边,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布沸手,位于F島的核電站,受9級(jí)特大地震影響注簿,放射性物質(zhì)發(fā)生泄漏契吉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一诡渴、第九天 我趴在偏房一處隱蔽的房頂上張望捐晶。 院中可真熱鬧,春花似錦妄辩、人聲如沸惑灵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)英支。三九已至,卻和暖如春哮伟,著一層夾襖步出監(jiān)牢的瞬間干花,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工楞黄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留池凄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓谅辣,卻偏偏與公主長(zhǎng)得像修赞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子桑阶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348