【設(shè)計模式】建造者模式(生成器模式)

建造者模式(生成器模式)

介紹

沒有人買車會只買一個輪胎或者方向盤,大家買的都是一輛包含輪胎、方向盤和發(fā)動機等多個部件的完整汽車。如何將這些部件組裝成一輛完整的汽車并返回給用戶,這是建造者模式需要解決的問題。建造者模式又稱為生成器模式,它是一種較為復(fù)雜、使用頻率也相對較低的創(chuàng)建型模式。建造者模式為客戶端返回的不是一個簡單的產(chǎn)品,而是一個由多個部件組成的復(fù)雜產(chǎn)品。

建造者模式是較為復(fù)雜的創(chuàng)建型模式,它將客戶端與包含多個組成部分(或部件)的復(fù)雜對象的創(chuàng)建過程分離,客戶端無須知道復(fù)雜對象的內(nèi)部組成部分與裝配方式,只需要知道所需建造者的類型即可。它關(guān)注如何一步一步創(chuàng)建一個的復(fù)雜對象,不同的具體建造者定義了不同的創(chuàng)建過程,且具體建造者相互獨立,增加新的建造者非常方便,無須修改已有代碼,系統(tǒng)具有較好的擴展性。

定義

將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。建造者模式是一種對象創(chuàng)建型模式。

UML類圖
角色
  • Builder(抽象建造者):它為創(chuàng)建一個產(chǎn)品Product對象的各個部件指定抽象接口,在該接口中一般聲明兩類方法,一類方法是buildPartX(),它們用于創(chuàng)建復(fù)雜對象的各個部件;另一類方法是getResult(),它們用于返回復(fù)雜對象。Builder既可以是抽象類,也可以是接口。
  • ConcreteBuilder(具體建造者):它實現(xiàn)了Builder接口,實現(xiàn)各個部件的具體構(gòu)造和裝配方法,定義并明確它所創(chuàng)建的復(fù)雜對象,也可以提供一個方法返回創(chuàng)建好的復(fù)雜產(chǎn)品對象。
  • Product(產(chǎn)品角色):它是被構(gòu)建的復(fù)雜對象,包含多個組成部件,具體建造者創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過程。
  • Director(指揮者):指揮者又稱為導(dǎo)演類,它負責安排復(fù)雜對象的建造次序,指揮者與抽象建造者之間存在關(guān)聯(lián)關(guān)系,可以在其construct()建造方法中調(diào)用建造者對象的部件構(gòu)造與裝配方法,完成復(fù)雜對象的建造。客戶端一般只需要與指揮者進行交互,在客戶端確定具體建造者的類型,并實例化具體建造者對象(也可以通過配置文件和反射機制),然后通過指揮者類的構(gòu)造函數(shù)或者Setter方法將該對象傳入指揮者類中。
demo代碼

在建造者模式的定義中提到了復(fù)雜對象,那么什么是復(fù)雜對象?簡單來說,復(fù)雜對象是指那些包含多個成員屬性的對象,這些成員屬性也稱為部件或零件,如汽車包括方向盤、發(fā)動機、輪胎等部件,電子郵件包括發(fā)件人、收件人、主題、內(nèi)容、附件等部件,一個典型的復(fù)雜對象類代碼示例如下:


class Product  {
       private  String partA; //定義部件,部件可以是任意類型,包括值類型和引用類型
       private  String partB;
       private  String partC;
       //partA的Getter方法和Setter方法省略
       //partB的Getter方法和Setter方法省略
       //partC的Getter方法和Setter方法省略
}

在抽象建造者類中定義了產(chǎn)品的創(chuàng)建方法和返回方法,其典型代碼如下:

abstract class Builder {
     //創(chuàng)建產(chǎn)品對象
       protected  Product product=new Product();
      
       public  abstract void buildPartA();
       public  abstract void buildPartB();
       public  abstract void buildPartC();
      
     //返回產(chǎn)品對象
       public  Product getResult() {
              return  product;
       }
}

在建造者模式的結(jié)構(gòu)中還引入了一個指揮者類Director,該類主要有兩個作用:一方面它隔離了客戶與創(chuàng)建過程;另一方面它控制產(chǎn)品的創(chuàng)建過程,包括某個buildPartX()方法是否被調(diào)用以及多個buildPartX()方法調(diào)用的先后次序等。指揮者針對抽象建造者編程,客戶端只需要知道具體建造者的類型,即可通過指揮者類調(diào)用建造者的相關(guān)方法,返回一個完整的產(chǎn)品對象。在實際生活中也存在類似指揮者一樣的角色,如一個客戶去購買電腦,電腦銷售人員相當于指揮者,只要客戶確定電腦的類型,電腦銷售人員可以通知電腦組裝人員給客戶組裝一臺電腦。指揮者類的代碼示例如下:

class Director {
       private  Builder builder;
      
       public  Director(Builder builder) {
              this.builder=builder;
       }
      
       public  void setBuilder(Builder builder) {
              this.builder=builer;
       }
      
     //產(chǎn)品構(gòu)建與組裝方法
       public Product construct() {
              builder.buildPartA();
              builder.buildPartB();
              builder.buildPartC();
              return builder.getResult();
       }
}

在指揮者類中可以注入一個抽象建造者類型的對象,其核心在于提供了一個建造方法construct(),在該方法中調(diào)用了builder對象的構(gòu)造部件的方法,最后返回一個產(chǎn)品對象。

對于客戶端而言,只需關(guān)心具體的建造者即可,一般情況下,客戶端類代碼片段如下所示:

Builder  builder = new ConcreteBuilder(); //可通過配置文件實現(xiàn)
Director director = new  Director(builder);
Product product = director.construct();
建造者模式與抽象工廠模式的比較

建造者模式與抽象工廠模式有點相似,但是建造者模式返回一個完整的復(fù)雜產(chǎn)品,而抽象工廠模式返回一系列相關(guān)的產(chǎn)品;在抽象工廠模式中,客戶端通過選擇具體工廠來生成所需對象,而在建造者模式中,客戶端通過指定具體建造者類型并指導(dǎo)Director類如何去生成對象,側(cè)重于一步步構(gòu)造一個復(fù)雜對象,然后將結(jié)果返回。如果將抽象工廠模式看成一個汽車配件生產(chǎn)廠,生成不同類型的汽車配件,那么建造者模式就是一個汽車組裝廠,通過對配件進行組裝返回一輛完整的汽車。

生成器 抽象工廠
構(gòu)建復(fù)雜對象 構(gòu)建簡單或復(fù)雜對象
以多個步驟構(gòu)建對象 以單一步驟構(gòu)建對象
以構(gòu)建過程的最后一步返回產(chǎn)品 立刻返回產(chǎn)品
專注一個特點產(chǎn)品 強調(diào)一套產(chǎn)品
關(guān)于Director的進一步討論

指揮者類Director在建造者模式中扮演非常重要的作用,簡單的Director類用于指導(dǎo)具體建造者如何構(gòu)建產(chǎn)品,它按一定次序調(diào)用Builder的buildPartX()方法,控制調(diào)用的先后次序,并向客戶端返回一個完整的產(chǎn)品對象。下面我們討論幾種Director的高級應(yīng)用方式:

1.省略Director

在有些情況下,為了簡化系統(tǒng)結(jié)構(gòu),可以將Director和抽象建造者Builder進行合并,在Builder中提供逐步構(gòu)建復(fù)雜產(chǎn)品對象的construct()方法。由于Builder類通常為抽象類,因此可以將construct()方法定義為靜態(tài)(static)方法。

class Builder {
      //創(chuàng)建產(chǎn)品對象
       protected  Product product=new Product();
      
       public  abstract void buildPartA();
       public  abstract void buildPartB();
       public  abstract void buildPartC();
      
      //返回產(chǎn)品對象
       public  Product getResult() {
              return  product;
       }
       
       // Build中增加construct方法,代替Director中的construct方法
       public static Product construct(Builder builder) {
              builder.buildPartA();
              builder.buildPartB();
              builder.buildPartC();
              return builder.getResult();
       }
}

客戶端類代碼片段如下所示:

Builder  builder = new ConcreteBuilder(); //可通過配置文件實現(xiàn)
Product product = builder.construct(builder);

除此之外,還有一種更簡單的處理方法,可以將construct()方法的參數(shù)去掉,直接在construct()方法中調(diào)用buildPartX()方法。

2.鉤子方法的引入

建造者模式除了逐步構(gòu)建一個復(fù)雜產(chǎn)品對象外,還可以通過Director類來更加精細地控制產(chǎn)品的創(chuàng)建過程,例如增加一類稱之為鉤子方法(HookMethod)的特殊方法來控制是否對某個buildPartX()的調(diào)用。鉤子方法的返回類型通常為boolean類型,方法名一般為isXXX(),鉤子方法定義在抽象建造者類中。

總結(jié)
優(yōu)點
  • 在建造者模式中,客戶端不必知道產(chǎn)品內(nèi)部組成的細節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過程解耦,使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品對象。
  • 每一個具體建造者都相對獨立,而與其他的具體建造者無關(guān),因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象。由于指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統(tǒng)擴展方便,符合“開閉原則”
  • 可以更加精細地控制產(chǎn)品的創(chuàng)建過程。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過程更加清晰,也更方便使用程序來控制創(chuàng)建過程。
缺點
  • 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點,其組成部分相似,如果產(chǎn)品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用范圍受到一定的限制。
  • 如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會導(dǎo)致需要定義很多具體建造者類來實現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大,增加系統(tǒng)的理解難度和運行成本。
適用場景
  • 需要生成的產(chǎn)品對象有復(fù)雜的內(nèi)部結(jié)構(gòu),這些產(chǎn)品對象通常包含多個成員屬性。
  • 需要生成的產(chǎn)品對象的屬性相互依賴,需要指定其生成順序。
  • 對象的創(chuàng)建過程獨立于創(chuàng)建該對象的類。在建造者模式中通過引入了指揮者類,將創(chuàng)建過程封裝在指揮者類中,而不在建造者類和客戶類中。
  • 隔離復(fù)雜對象的創(chuàng)建和使用,并使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

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