日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

从零开始学习Java设计模式 | 创建型模式篇:建造者模式

發布時間:2024/1/8 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从零开始学习Java设计模式 | 创建型模式篇:建造者模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在本講,我們來學習一下創建型模式里面的最后一個設計模式,即建造者模式。

概述

建造者模式是指將一個復雜對象的構建與表示分離,使得同樣的構建過程可以創建不同的表示。

讀完這句話之后,我估計很多人都已經懵了,這說的是什么意思啊?哈哈哈😊,別急,下面我會給大家解釋一下,先看一下下面這張圖。

上圖右邊是一個主機,而左邊則是主機里面包含的部件(或者組件),有主板、cpu、內存條、風扇、硬盤以及電源。

結合這張圖,我來向大家解釋一下建造者模式的概念。建造者模式的概念中不是有一個復雜對象嗎?你可以將其理解為上圖中的主機。為什么把它稱為復雜對象呢?因為主機里面是包含了很多很多部件的。其實,建造者模式說的就是將主機和主機里面的部件進行了一個分離,分離之后有什么好處呢?解除耦合唄!另外,你可以把上圖中的那些組件裝配成一個主機的過程理解為建造者模式概念中的構建過程。

而同樣的構建過程,也即不同的裝配方式,最終創建出來的復雜對象(即主機)是不同的。這個如何去理解呢?例如,我現在主板、cpu、內存條都換了一個品牌,那么這時同樣的一個裝配過程最終出來的主機肯定和之前的是不一樣的,你想啊,我現在的內存條選擇的是16G,之前有可能選擇的是8G,那現在裝配的主機肯定和之前的是不一樣的啊!

經過我上面的解釋,相信大家一定理解了建造者模式。下面,我們再來看一下有關建造者模式的具體的描述。

  • 建造者模式分離了部件的構造(由Builder來負責構造,Builder是建造者模式中的一個角色)和裝配(由Director負責裝配,Director也是建造者模式中的一個角色),從而就可以構造出復雜的對象了,而且我們使用建造者模式最終要的就是這個復雜對象。這個模式適用于某個對象的構建過程復雜的情況,若是這種情況,則可以使用建造者模式

  • 由于實現了構建和裝配的解耦,不同的構建器,相同的裝配,也可以做出不同的對象,就拿我上面舉的例子來說,如果構成主機組件的品牌發生了變化,那么以同樣的方式進行組裝,出來的肯定是不同的對象;相同的構建器,不同的裝配順序也可以做出不同的對象。也就是實現了構建算法、裝配算法的解耦,這樣就實現了更好的復用

  • 建造者模式可以將部件和其組裝過程分開,一步一步創建一個復雜的對象。用戶只需要指定復雜對象的類型就可以得到該對象,而無須知道其內部的具體構造細節。

    這就跟我們去買一臺電腦一樣,我們不關注這臺電腦里面用的每個品牌的組件以及它是如何進行裝配的,我們只關注最終買的是一臺電腦就行了

相信讀到這,大家對建造者模式又有了一個深刻的認識。

結構

建造者(Builder)模式包含有如下角色:

  • 抽象建造者類(Builder):這個接口規定要實現復雜對象的哪些部分的創建,并不涉及具體的部件對象的創建,而是由具體建造者類來實現。

    注意了,這里說的接口并不是我們通常在Java里面定義的接口,而是指的是一種規范,既可以是Java里面的接口,又可以是抽象類,希望大家能明白這點~

  • 具體建造者類(ConcreteBuilder):實現Builder接口,完成復雜產品的各個部件的具體創建方法。在構造過程完成后,提供產品的實例。

    注意了,建造者模式的核心思想并不在于復雜對象各個部件的創建,而是強調的是整個裝配的過程,至于對于這些部件的創建,我們既可以使用工廠方法模式,又可以使用抽象工廠模式

  • 產品類(Product):要創建的復雜對象

  • 指揮者類(Director):調用具體建造者來創建復雜對象的各個部分,在指揮者中不涉及具體產品的信息,它只負責保證對象各個部分完整創建或按某種順序創建。

    也就是說,指揮者所要完成的工作就是裝配復雜對象。舉個例子,小妹需要一臺電腦來學Java,她想要我幫她組裝一臺電腦,在組裝電腦的過程中,我是不管cpu是什么牌子的,至于這個cpu是怎么去構建的,究竟是小妹自己買的還是自己造的,我也通通不管,我就只負責裝配,很顯然這里指揮者類就是指我本人

了解了建造者模式里面包含的角色之后,接下來我們便來看一下下面的類圖,根據以下類圖我會再為大家講解一下建造者模式。

從以上類圖中可以看到,在抽象建造者類里面構建了各個部分,比如buildPartA方法來構建A部分,buildPartB方法來構建B部分,而且這些方法都是抽象的,需要由具體的建造者去實現,也即構建具體的部件;再來看一下指揮者類,注意看,它是將Builder聚合進來了,而且在construct這個組裝方法里面,還會將各個部件組裝成一個復雜對象喲;最后看一下具體建造者類,它除了要重寫抽象建造者類里面的抽象方法之外,還提供了一個獲取復雜對象的方法,即getResult,調用該方法我們就可以獲取到復雜對象。

以上類圖分析完了之后,接下來我們就通過一個案例再去深入地理解一下建造者模式,而這個案例就是創建共享單車,也就是生產自行車。

案例

生產自行車是一個復雜的過程,它包含了車架、車座等組件的生產。而車架又有碳纖維、鋁合金等材質,車座有橡膠、真皮等材質。這樣,對于自行車的生產,我們就可以使用建造者模式了,因為生產的是自行車這個復雜對象。

很明顯,對于該案例而言,自行車就是一個復雜對象,因為它里面包含有很多組件,而現在的組件只有車架和車座,但是肯定還有其他的一些組件,例如輪胎、輪轂等等,只不過在該案例里面我們就不進行模擬了,而只是通過車架和車座這兩個組件來簡單的模擬一下就可以了。

下面我們就來分析一下該案例里面要涉及到哪些類。

  • 產品類:Bike類,包括了車架、車座等組件。注意了,在該案例中,我為了簡單,就不再將車架和車座單獨設計成對應的類了,而是直接以字符串的形式體現出來
  • 抽象建造者類:Builder類,在該案例中,我是將其設計成了抽象類
  • 具體建造者類:抽象建造者類的子實現類,在該案例中,我設計出來了兩個子實現類,分別是MobileBuilder(用來構建摩拜單車)和OfoBuilder(用來構建ofo單車)
  • 指揮者類:Director類,它是用來裝配自行車的

明確了該案例里面所涉及到的類之后,你能不能畫出下面這樣一個類圖呢?要是畫不出來,就看下面這張類圖吧!也不礙事。

先來看第一個類吧,即Client(客戶端),它是需要依賴于其他類的,我們先暫時不去關注它。

再來看一下Bike類(即產品類),可以看到它里面聲明了表示車架和車座這倆組件的屬性,不過這兒我們直接是以字符串的形式體現出來了,并且還為其提供了對應的setter和getter方法。

看完產品類之后,我們再來看一下Builder類,可以看到它里面聲明了一個Bike類型的變量,這就相當于是聚合了Bike類型的對象,此外,它里面還提供了三個抽象方法,一個buildFrame方法用于構建車架,一個buildSeat方法用于構建車座,一個createBike方法用于創建自行車,正是由于該方法是用于創建自行車的,所以該方法的返回值的類型是Bike。大家一定要注意了,由于Builder類是抽象建造者類(這里我將其設計成了抽象類),所以它里面提供的三個方法都是抽象方法。

正是由于Builder類是抽象建造者類,所以我們還要提供對應的具體建造者類,從以上類圖中可以看到,這兒我們提供了兩個具體建造者類,分別是MobileBuilder和OfoBuilder,它倆肯定是要重寫父類中的三個抽象方法的。

最后,大家千萬不要忘了最重要的一個類,即Director,它聚合了Builder類,因為它得通過Builder類去構建具體的自行車,而且通過該類中的construct方法,我們還可以去控制自行車組裝的一個過程,因為該方法就是用于組裝自行車的。此外,在該類里面我們還提供了一個有參構造,這樣,該類就沒有無參構造了。

以上類圖我就為大家分析至此了,接下來,我們就要編寫代碼來實現以上生產自行車的案例了。

首先,打開咱們的maven工程,在com.meimeixia.pattern包下新建一個子包,即builder.demo1,實現以上生產自行車案例的代碼我們都放在了該包下。

然后,創建產品類,即Bike。

package com.meimeixia.pattern.builder.demo1;/*** 產品對象* @author liayun* @create 2021-06-02 21:48*/ public class Bike {private String frame; // 車架private String seat; // 車座public String getFrame() {return frame;}public void setFrame(String frame) {this.frame = frame;}public String getSeat() {return seat;}public void setSeat(String seat) {this.seat = seat;} }

接著,創建抽象建造者類,即Builder。

package com.meimeixia.pattern.builder.demo1;/*** @author liayun* @create 2021-06-03 5:16*/ public abstract class Builder {// 聲明Bike類型的變量,并進行賦值protected Bike bike = new Bike();public abstract void buildFrame();public abstract void buildSeat();// 構建自行車的方法public abstract Bike createBike();}

從以上代碼中可以看到,我們是在Builder類中聲明了一個Bike類型的變量,并為其進行了賦值,這是為啥呢?其實從以上類圖中我們也知道應該這么做,它說得很明白了,Builder類是要聚合Bike類型的對象的。

其二,是因為這樣做可以提高代碼的一個復用性,為什么這么說呢?還是看一下以上類圖,你會發現我們得在具體建造者類(比如MobileBuilder和OfoBuilder)里面構建自行車,既然這樣,那么它里面就得有一個自行車了,所以我們就把聲明一個Bike類型變量這件事放在父類(即抽象建造者類)中來做了,這樣做的話,就可以提高代碼的一個復用性了。

此外,大家還要注意一點,在為Bike類型的變量賦值時,我們是new了一個Bike類型的對象,而我想要告訴大家的是,該對象里面的組件(車架和車座)還并未組裝,就跟我們組裝一臺主機一樣,現在是只有一個機箱。如果要想給該對象組裝組件,那么這件事就得交給具體的指揮者去做了。

緊接著,創建具體建造者類。第一個類是MobileBuilder,它得繼承以上Builder類并重寫里面所有的抽象方法。

package com.meimeixia.pattern.builder.demo1;/*** 具體的構建者,用來構建摩拜單車對象* @author liayun* @create 2021-06-03 5:26*/ public class MobileBuilder extends Builder {/*** 構建車架*/@Overridepublic void buildFrame() {bike.setFrame("碳釬維");}/*** 構建車座*/@Overridepublic void buildSeat() {bike.setSeat("真皮");}@Overridepublic Bike createBike() {return bike;} }

第二個類是OfoBuilder,同樣,它也得繼承以上Builder類并重寫里面所有的抽象方法。

package com.meimeixia.pattern.builder.demo1;/*** ofo單車構建者,用來構建ofo單車* @author liayun* @create 2021-06-03 5:31*/ public class OfoBuilder extends Builder {/*** 構建車架*/@Overridepublic void buildFrame() {bike.setFrame("鋁合金");}/*** 構建車座*/@Overridepublic void buildSeat() {bike.setFrame("橡膠");}@Overridepublic Bike createBike() {return bike;} }

具體建造者類創建完畢之后,接下來,我們來創建指揮者類,即Director。

package com.meimeixia.pattern.builder.demo1;/*** 指揮者類* @author liayun* @create 2021-06-03 5:40*/ public class Director {// 聲明Builder類型的變量private Builder builder; // 在去創建Director對象的時候再為其賦值,所以在Director類里面我們得提供一個有參構造public Director(Builder builder) {this.builder = builder;}// 組裝自行車的功能public Bike construct() {builder.buildFrame();builder.buildSeat();return builder.createBike();} }

最后,編寫一個測試類來進行測試,測試類的名稱我們不妨就叫做Client。

package com.meimeixia.pattern.builder.demo1;/*** @author liayun* @create 2021-06-03 5:48*/ public class Client {public static void main(String[] args) {// 創建指揮者對象,此刻我們要生產的是摩拜單車,所以在Director類的有參構造中我們傳入的是MobileBuilder對象Director director = new Director(new MobileBuilder());// 讓指揮者指揮組裝自行車Bike bike = director.construct();System.out.println(bike.getFrame());System.out.println(bike.getSeat());} }

運行以上測試類,打印結果如下,這說明咱們的摩拜單車已經組裝好了。

從上可以看到,對于客戶端來說,它并不需要去關注底層實現,即復雜對象(Bike對象)的構建過程以及構建算法,而是直接調用指揮者類中的功能就可以構建出一個復雜對象了,繼而客戶端就能使用該復雜對象了。

這里,我還得給大家說一個注意事項,就是上面示例是Builder模式的常規用法,指揮者類 (即Director)在建造者模式中具有很重要的作用,它用于指導具體建造者如何構建產品,控制調用先后次序,并向調用者返回完整的產品類,但是有些情況下需要簡化系統結構,比如說,對于上面示例而言,整個的系統結構還是比較復雜的,這樣,對于程序員的要求是比較高的,所以我們可以去做簡化,怎么去簡化呢?這里我們可以這樣去做,即把指揮者類和抽象建造者類進行結合,也就是說我們把指揮者類中的功能都給它加到抽象建造者類中,也即我們就不再需要指揮者類了。

來看下面這段代碼,它就是抽象建造者類,和上面我們創建的幾乎一模一樣,只不過是給它里面多加了一個組裝自行車的功能,這樣,就不需要具體的指揮者類了,這也算是建造者模式的一種改進吧!對于這種改進,我就不編寫具體的代碼來進行演示了,大家如果有興趣的話,不妨自己嘗試著去做一下。

// 抽象建造者類 public abstract class Builder {protected Bike bike = new Bike();public abstract void buildFrame();public abstract void buildSeat();public abstract Bike createBike();public Bike construct() {this.buildFrame();this.BuildSeat();return this.createBike();}}

不過,大家還得注意一點,就是這樣做確實是簡化了系統結構,但同時也加重了抽象建造者類的職責(不光要構建組件,還要進行組裝,所以任務還是比較多的),也不是太符合單一職責原則,如果construct方法過于復雜,那么建議還是封裝到Director類中。

優缺點

通過以上案例,相信大家對于建造者模式的理解應該更加深刻了。接下來,我們便來探討一下建造者模式的優缺點。

優點

第一個,建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化,在使用建造者模式的場景中,一般產品類和建造者類是比較穩定的,因此,將主要的業務邏輯封裝在指揮者類中對整體而言可以取得比較好的穩定性。

這段話怎么去理解呢?產品類和建造者類一般而言是比較穩定的,唯一發生變化的就是指揮者類中組裝的那個過程,如果它經常發生變化,那么我們只需要去修改指揮者類里面的代碼就可以了。

第二個,在建造者模式中,客戶端不必知道產品內部組成的細節,直接調用指揮者類里面的組裝方法就可以組裝一個產品出來,這樣就將產品本身與產品的創建過程解耦了,使得相同的創建過程可以創建不同的產品對象。其實這個我也一直在強調,組裝過程一樣,如果需要的組件發生了變化,那么生產出來的產品肯定是不一樣的。

第三個,可以更加精細地控制產品的創建過程。將復雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程序來控制創建過程。

從以上案例中你就能清晰地知道,我們確實是將復雜對象的創建步驟分解在不同的方法里面了,你要是不信的話,不妨再看一下Builder類的代碼,你會發現它里面的buildFrame和buildSeat這倆方法創建的都是不同的組件。最終,客戶端就會調用指揮者類中的組裝方法進行一個組裝,便生成了一個復雜對象。

第四個,建造者模式很容易進行擴展。如果有新的需求,通過實現一個新的建造者類就可以完成(如果以上例為例,我現在要生產另外一個品牌的單車,那么我只需要再去創建一個新的建造者類就可以了),基本上不用修改之前已經測試通過的代碼,因此也就不會對原有功能引入風險了,這也符合了開閉原則。

缺點

建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,若產品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到了一定的限制。

就拿上例來說,該案例生產的是自行車,如果現在要去生產一臺電腦的話,那么此情此景下肯定是不適合使用建造者模式的,因為組裝產品的組件都不一樣。當然,雖說以上案例中生產的是自行車,但是我們可以再對其進行一個抽象(即抽取共有的),然后再去創建不同的子類,不過,我們在這里面并沒有這樣去做。

使用場景

建造者(Builder)模式創建的是復雜對象,其產品的各個部分經常面臨著劇烈的變化,但將它們組合在一起的算法卻相對穩定,所以它通常在以下場合使用。

  • 創建的對象較復雜,由多個部件構成,各部件面臨著復雜的變化,但構件間的建造順序是穩定的
  • 創建復雜對象的算法獨立于該對象的組成部分以及它們的裝配方式,即產品的構建過程和最終的表示(即最終創建出來的復雜對象)是獨立的,也就是說復雜對象裝配的算法和其里面的組件是分離的

根據以上兩條法則,大家就可以去判斷什么樣的場景下適合使用建造者模式了。

擴展

在這一小節,我們對建造者模式進行一個擴展。

建造者模式除了上面的用途外,在開發中還有一個常用的使用方式,就是當一個類構造器需要傳入很多參數時,如果創建這個類的實例,代碼可讀性就會非常差,而且還很容易引入錯誤,那么此時就可以利用建造者模式進行重構了。

我們先不管利用建造者模式進行重構這件事,而是先來看一下最原始的設計方式,就拿我們現在要設計一個手機類來說事,手機里面是有很多組件的,例如cpu、屏幕、內存、主板等等,這樣,當我們去創建一個手機對象時,肯定是要設置這些組件的數據的。不難想到在設計手機類時,我們應該提供了一個有參構造方法,而且方法里面的參數應有cpu、屏幕、內存以及主板等。

public class Phone {private String cpu;private String screen;private String memory;private String mainboard;public Phone(String cpu, String screen, String memory, String mainboard) {this.cpu = cpu;this.screen = screen;this.memory = memory;this.mainboard = mainboard;}public String getCpu() {return cpu;}public void setCpu(String cpu) {this.cpu = cpu;}public String getScreen() {return screen;}public void setScreen(String screen) {this.screen = screen;}public String getMemory() {return memory;}public void setMemory(String memory) {this.memory = memory;}public String getMainboard() {return mainboard;}public void setMainboard(String mainboard) {this.mainboard = mainboard;}@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';} }

以上Phone類的代碼還是比較簡單的,下面我們主要來看一下客戶端的代碼。

public class Client {public static void main(String[] args) {// 構建Phone對象Phone phone = new Phone("intel", "三星屏幕", "金士頓", "華碩");System.out.println(phone);} }

大家看清楚在構建手機對象時所需要傳入的參數了吧!現在還只是需要傳入四個參數,如果需要傳遞更多的參數,那么你會發現寫出來的代碼的可讀性將會非常差,并且使用成本也會比較高。所以,在這種情況下,我們就可以選擇使用建造者模式對其進行重構了。那如何做到這一點呢,下面我就會講到。

首先,在com.meimeixia.pattern.builder包下新建一個子包,即demo2,使用建造者模式進行重構的代碼我們都放在了該包下。

然后,創建一個手機類,即Phone。由于該類的創建比較復雜,所以下面我會分步驟來為大家進行講解。

第一步,在該手機類里面提供四個成員變量,分別用以表示cpu、屏幕、內存、主板等手機組件。

package com.meimeixia.pattern.builder.demo2;/*** 手機類* @author liayun* @create 2021-06-03 6:41*/ public class Phone {private String cpu;private String screen;private String memory;private String mainboard;// ... }

第二步,由于我們現在是使用建造者模式來進行重構,所以就不能讓外界直接調用Phone類的構造方法了,也就是說我們應該私有Phone類的構造方法。那么構造方法里面需不需要傳遞什么參數呢?需要,需要傳遞一個構建器。

package com.meimeixia.pattern.builder.demo2;/*** 手機類* @author liayun* @create 2021-06-03 6:41*/ public class Phone {private String cpu;private String screen;private String memory;private String mainboard;// 私有構造方法private Phone(Builder builder) { // 構造方法里面需要傳入一個構建器// ...} }

第三步,定義Builder構建器。在哪定義呢?就在Phone類中定義一個靜態的內部類,Builder內部類定義完畢之后,我們還得在它里面提供四個成員變量,分別用以表示cpu、屏幕、內存、主板等手機組件,并且還要提供對應的方法進行這些組件的構建喲!

package com.meimeixia.pattern.builder.demo2;/*** 手機類* @author liayun* @create 2021-06-03 6:41*/ public class Phone {private String cpu;private String screen;private String memory;private String mainboard;// 私有構造方法private Phone(Builder builder) { // 構造方法里面需要傳入一個構建器// ...}public static final class Builder {private String cpu;private String screen;private String memory;private String mainboard;/*** 大家可以看到,方法名是和以上成員變量(或者屬性)的名稱保持一致的,并且方法最終返回的就是咱們的Builder* @param cpu* @return*/public Builder cpu(String cpu) {this.cpu = cpu; // 把方法中的參數設置給成員變量return this; // 因為方法的返回值類型是Builder,所以我們得返回一個Builder對象,這樣,不妨直接返回當前對象就哦了}public Builder screen(String screen) {this.screen = screen;return this;}public Builder memory(String memory) {this.memory = memory;return this;}public Builder mainboard(String mainboard) {this.mainboard = mainboard;return this;}// ...}}

第四步,完善Phone類中的構造方法,即把構建者對象中的成員變量直接賦給Phone類中的成員變量。

package com.meimeixia.pattern.builder.demo2;/*** 手機類* @author liayun* @create 2021-06-03 6:41*/ public class Phone {private String cpu;private String screen;private String memory;private String mainboard;// 私有構造方法private Phone(Builder builder) { // 構造方法里面需要傳入一個構建器this.cpu = builder.cpu;this.screen = builder.screen;this.memory = builder.memory;this.mainboard = builder.mainboard;}public static final class Builder {private String cpu;private String screen;private String memory;private String mainboard;/*** 大家可以看到,方法名是和以上成員變量(或者屬性)的名稱保持一致的,并且方法最終返回的就是咱們的Builder* @param cpu* @return*/public Builder cpu(String cpu) {this.cpu = cpu; // 把方法中的參數設置給成員變量return this; // 因為方法的返回值類型是Builder,所以我們得返回一個Builder對象,這樣,不妨直接返回當前對象就哦了}public Builder screen(String screen) {this.screen = screen;return this;}public Builder memory(String memory) {this.memory = memory;return this;}public Builder mainboard(String mainboard) {this.mainboard = mainboard;return this;}// ...}}

第五步,由于Phone類中的構造方法私有了,所以外界是不能直接調用Phone類的構造方法去創建Phone對象的。既然這樣,那么我們只能使用Builder內部類去構建Phone對象了。因此,我們還得在Builder內部類里面定義一個創建Phone對象的方法。

package com.meimeixia.pattern.builder.demo2;/*** 手機類* @author liayun* @create 2021-06-03 6:41*/ public class Phone {private String cpu;private String screen;private String memory;private String mainboard;// 私有構造方法private Phone(Builder builder) { // 構造方法里面需要傳入一個構建器this.cpu = builder.cpu;this.screen = builder.screen;this.memory = builder.memory;this.mainboard = builder.mainboard;}public static final class Builder {private String cpu;private String screen;private String memory;private String mainboard;/*** 大家可以看到,方法名是和以上成員變量(或者屬性)的名稱保持一致的,并且方法最終返回的就是咱們的Builder* @param cpu* @return*/public Builder cpu(String cpu) {this.cpu = cpu; // 把方法中的參數設置給成員變量return this; // 因為方法的返回值類型是Builder,所以我們得返回一個Builder對象,這樣,不妨直接返回當前對象就哦了}public Builder screen(String screen) {this.screen = screen;return this;}public Builder memory(String memory) {this.memory = memory;return this;}public Builder mainboard(String mainboard) {this.mainboard = mainboard;return this;}// 使用構建者創建Phone對象public Phone build() {// 在內部類中是可以直接去訪問外部類中私有的方法的return new Phone(this); // 記住,我們要把當前對象(即this)給其傳遞過去,因為Phone類// 中只提供了一個構造方法,而且方法參數要的還是Builder對象}}@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';}}

注意,在Phone類中千萬不要忘了重寫toString方法喲,因為待會我們測試的時候便會用到。

至此,Phone類才算是創建完畢了。

創建完畢之后,接下來,我們就得創建一個測試類來進行測試了,在該測試類中我們如何去創建一個Phone對象呢?現在我們是不能直接去創建Phone對象的,而只能是通過Builder內部類來創建,如下所示。

package com.meimeixia.pattern.builder.demo2;/*** @author liayun* @create 2021-06-21 16:47*/ public class Client {public static void main(String[] args) {// 創建手機對象 通過構建者對象獲取手機對象Phone phone = new Phone.Builder().cpu("intel") // 由于cpu、screen等這些方法返回的都是當前對象,所以我們就可以使用鏈式編程了.screen("三星屏幕").memory("金士頓內存條").mainboard("華碩主板").build();System.out.println(phone);} }

此時,運行以上測試類,可以看到打印結果如下,Phone對象確實是創建成功了,并且還為它里面的組件進行了賦值。

以上就是建造者模式的另外一種用法。這種做法和我們之前的原始方式有什么區別呢?

  • 第一個區別:構建哪個組件,我們是一目了然的。例如調用cpu方法,我們就知道要進行組裝的就是cpu
  • 第二個區別:對于原始的建造者模式來說,組件的構建順序是由指揮者類來定奪的,而現在是把組件的構建順序交給了客戶,客戶想怎么去構建,就按照他的順序去構建就行了

最后,我得多說一嘴,就是重構后的代碼使用起來更方便,某種程度上也可以提高開發效率,因為我們可以鏈式編程了,而且代碼的可讀性比我們之前的程序更高了。此外,從軟件設計上來說,對程序員的要求就比較高了,因為你能明顯看到我們在設計以上Phone類時,比我們之前設計的要復雜太多,但是用起來很方便,嘿嘿!

創建型模式各個模式的對比

建造者模式講完了之后,創建型模式我們就已經全部講解完畢了。想必大家都知道創建型模式總共包含了五個設計模式,它們分別是:

  • 單例設計模式
  • 工廠方法模式
  • 抽象工廠模式
  • 原型模式
  • 建造者模式
  • 相信大家對于單例設計模式和原型模式應該不會混淆,但是對于其他的一些設計模式或許就會搞混了,因為它們在使用上面還是比較相似的。不知你在學習這些設計模式時,心中是否有一個小小的疑問,那就是它們好像在做同一件事耶!

    所以,在這一小節,我有必要專門花些時間對工廠方法模式、抽象工廠模式以及建造者模式做一個對比說明。下面我們先來說一下工廠方法模式和建造者模式的一個區別。

    工廠方法模式 VS 建造者模式

    工廠方法模式注重的是整體對象的創建方式;而建造者模式注重的是部件構建的過程,意在通過一步一步地精確構造創建出一個復雜的對象。

    我們舉個簡單例子來說明兩者的差異,現在要制造一個超人,如果使用工廠方法模式,那么直接生產出來的就是一個力大無窮、能夠飛翔、內褲外穿的超人;而若使用建造者模式,則需要組裝手、頭、腳、軀干等部分,然后再把內褲外穿,于是一個超人就誕生了。

    所以,工廠方法模式和建造者模式這兩者的側重點是不一樣的,工廠方法模式側重的是整體對象的創建,而建造者模式側重的是部件構建的過程,你只要牢牢記住這一點就行了。

    抽象工廠模式 VS 建造者模式

    抽象工廠模式實現對產品家族的創建,一個產品家族是這樣的一系列產品:具有不同分類維度的產品組合,采用抽象工廠模式則是不需要關心構建過程,只關心什么產品由什么工廠生產就可以了。

    舉個例子來說,現在我要去組裝一臺計算機,那么計算機里面得有這么一些組件,如主板、硬盤、內存條、電源以及cpu等等,這些組件就可以理解成是一個產品家族,而抽象工廠模式就是用來生產這些產品家族里面的產品的。

    建造者模式則是要求按照指定的藍圖建造產品,它的主要目的是通過組裝零配件而產生一個新產品。

    如果將抽象工廠模式看成汽車配件生產工廠,生產一個產品族的產品,那么建造者模式就是一個汽車組裝工廠,通過對部件的組裝可以返回一輛完整的汽車。這樣的話,你會發現,我們可以把抽象工廠模式和建造者模式整合到一塊來設計汽車的生產過程,因為汽車配件可以使用抽象工廠模式來生產,而汽車的組裝可以使用建造者模式來進行組裝,如此就可形成一個完整的系統了。

    所以,大家一定要注意了,我們以后真正去用設計模式的話,并不意味著只在某一種情況下只用一種設計模式,而更有可能是多種設計模式混合到一塊去使用。

    總結

    以上是生活随笔為你收集整理的从零开始学习Java设计模式 | 创建型模式篇:建造者模式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。