1、嘻哈說
首先,請您欣賞建造者模式的原創歌曲。
嘻哈說:建造者模式
作曲:懶人
作詞:懶人
Rapper:懶人
將一個複雜物件的構建與它的表示分離
使得同樣構建過程可以建立不同的表示
真是一步一步建立一個複雜物件的過程好似紳士
我在絞盡腦汁想著怎麼包餃子
先皮再陷的構建過程不會存在貓膩
但皮和陷各有不同需要各位老師傅
我就是導演者,負責構建的整個過程
老師傅呢,是具體建造者,建造每個部件幹活不磨蹭
餃子是產品角色,由一系列部件組成,不陌生
對了,記得還有抽象建造者
這樣才能說聲建造者模式可用
或者產品類中有個靜態內部類Builder
這種方式算是折中
複製程式碼
閒來無事聽聽曲,知識已填腦中去;
學習複習新方式,頭戴耳機不小覷。
番茄課堂,學習也要酷。
2、定義
無論是在現實世界中還是在軟體系統中,都存在一些複雜的物件,它們擁有多個組成部分,如電腦,它包括CPU、硬碟、記憶體等各種部件。
對於大多數使用者而言,無須知道這些部件的裝配細節,也幾乎不會使用單獨某個部件,而是完整的一臺電腦。
由於需求的變化,這個複雜物件的各個部分經常面臨著劇烈的變化,但是將它們組合在一起的演算法確相對穩定。如何應對這種變化?
如何提供一種“封裝機制”來隔離出“複雜物件的各個部分”的變化,從而保持系統中的 “穩定構建演算法”不隨著需求改變而改變?
這就是要說的建造者模式。
使用建造者模式,使用者只需要指定複雜物件的型別就可以得到該物件,而無須知道其內部的具體構造細節。
我們來看一下建造者模式的定義。
將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
怎麼理解呢?
首先,是一個複雜的物件,並且將它的構造和表示分開。
這樣做的目的是可以單獨管理構建的過程,避免了呼叫者關心建立物件內部的邏輯或者順序。
而且還可以用一樣的構造過程建立不同的表示。
它既然是用來建立物件,那肯定就是建立型模式。
3、場景
美食餃子
番茄餐廳的後廚。
廚師長:最近有不少客戶反饋,我們們的餃子上菜速度比較慢。我呢,重新規劃了一下流程,首先擀餃子皮,然後絞餃子餡,最後搞熟。
廚師A:好的。
廚師長:你們不用管這個流程,我來負責流程。你們就負責擀餃子皮,絞餃子餡,還有搞熟。我讓你們做哪步就做哪步。明白沒?
廚師A:明白了。
廚師長:明白了還不趕緊幹活去,這一天天的,什麼時候能像我一樣,求生欲這麼強,我們們上菜速度就上去了。
4、建造者模式
建造者模式的UML類圖。
package com.fanqiekt.builder;
/**
* 餃子
*
* @Author: 番茄課堂-懶人
*/
public class Dumpling {
private String wrapper; //餃子皮
private String stuffing; //餃子餡
public String getWrapper() {
return wrapper;
}
public void setWrapper(String wrapper) {
this.wrapper = wrapper;
}
public String getStuffing() {
return stuffing;
}
public void setStuffing(String stuffing) {
this.stuffing = stuffing;
}
@Override
public String toString() {
return "餃子皮是" + wrapper + ",餃子餡是" + stuffing;
}
}
複製程式碼
Dumpling:它是產品(Product)角色,由一系列部件組成。
一般是一個較為複雜的物件,也就是說建立物件的過程比較複雜,一般會有比較多的程式碼量。
在本類圖中,產品類是一個具體的類,而非抽象類。
實際程式設計中,產品類可以是由一個抽象類與它的不同實現組成,也可以是由多個抽象類與他們的實現組成。
package com.fanqiekt.builder;
/**
* 抽象的Builder類
*
* @Author: 番茄課堂-懶人
*/
public interface Builder {
// 餃子皮
void buildWrapper(String wrapper);
// 餃子餡
void buildStuffing(String stuffing);
// 建立Dumpling
Dumpling build();
}
複製程式碼
Builder:它是抽象建造者(Builder)角色,給出一個抽象介面,以規範產品物件的各個組成成分的建造。
一般而言,此介面獨立於應用程式的業務邏輯。
模式中直接建立產品物件的是具體建造者 (ConcreteBuilder)角色。
一般來說,產品所包含的零件數目與建造方法的數目相符。換言之,有多少零件,就有多少相應的建造方法。
引入抽象建造者的目的,是為了將建造的具體過程交與它的子類來實現,方便擴充套件其他的建造方式。
package com.fanqiekt.builder;
/**
* 具體的 Builder類, 水餃的Builder
*
* @Author: 番茄課堂-懶人
*/
public class DumplingBuilder implements Builder{
private Dumpling dumpling = new Dumpling();
@Override
public void buildWrapper(String wrapper) {
System.out.println("製作餃子皮:" + wrapper);
dumpling.setWrapper(wrapper);
}
@Override
public void buildStuffing(String stuffing) {
System.out.println("製作餃子陷:" + stuffing);
dumpling.setStuffing(stuffing);
}
@Override
public Dumpling build() {
System.out.println("下鍋煮餃子");
return dumpling;
}
}
複製程式碼
DumplingBuilder:它是具體建造者(ConcreteBuilder)角色。
實現抽象類的所有未實現的方法,具體來說一般是兩項任務:組建產品;返回組建好的產品。
它就相當於場景中的廚師A。
package com.fanqiekt.builder;
/**
* 導演者
*
* @Author: 番茄課堂-懶人
*/
public class Director {
Builder builder;
public Director(Builder builder){
this.builder = builder;
}
public void constructDumpling(String wrapper, String stuffing){
builder.buildWrapper(wrapper);
builder.buildStuffing(stuffing);
}
}
複製程式碼
Director:它是導演者(Director)角色,負責呼叫適當的建造者來組建產品。
導演類一般不與產品類發生依賴關係,與導演類直接互動的是建造者類。
一般來說,導演類被用來封裝程式中易變、及其複雜的部分。
package com.fanqiekt.builder;
/**
* 客戶端
*
* @Author: 番茄課堂-懶人
*/
public class Client {
public static void main(String args[]){
Builder builder = new DumplingBuilder();
Director director = new Director(builder);
director.constructDumpling("白麵餃子皮", "豬肉大蔥餡");
Dumpling dumpling = builder.build();
System.out.println("餃子出鍋嘍:" + dumpling.toString());
}
}
複製程式碼
導演者角色是與客戶端打交道的角色。
導演者將客戶端建立產品的請求劃分為對各個零件的建造請求,再將這些請求委派給具體建造者角色。
具體建造者角色是做具體建造工作的,但是卻不為客戶端所知。
在實際應用中,很多會把該角色省略。
他就相當於場景中求生欲極強的廚師長。
我們可以執行下,看一下結果。
製作餃子皮:白麵餃子皮
製作餃子陷:豬肉大蔥餡
下鍋煮餃子
餃子出鍋嘍:餃子皮是白麵餃子皮,餃子餡是豬肉大蔥餡
複製程式碼
如果我現在不止想做水餃,我還想做個蒸餃怎麼辦?
很簡單,直接建立個新的Builder。
package com.fanqiekt.builder;
/**
* 具體的 Builder類, 蒸餃的Builder
*
* @Author: 番茄課堂-懶人
*/
public class SteamedDumplingBuilder implements Builder{
private Dumpling dumpling = new Dumpling();
@Override
public void buildWrapper(String wrapper) {
System.out.println("製作餃子皮:" + wrapper);
dumpling.setWrapper(wrapper);
}
@Override
public void buildStuffing(String stuffing) {
System.out.println("製作餃子陷:" + stuffing);
dumpling.setStuffing(stuffing);
}
@Override
public Dumpling build() {
System.out.println("放在蒸籠上蒸餃子");
return dumpling;
}
}
複製程式碼
這就是抽象建造者的作用了。
5、建造者模式變種
您是不是感覺建造者角色太多,太麻煩了?
這個時候,我們建造者的變種就該登臺亮相了。
建造者模式變種的UML類圖。
package com.fanqiekt.builder.varietas;
/**
* 餃子
*
* @Author: 番茄課堂-懶人
*/
public class Dumpling {
Params params = new Params();
private Dumpling(){
}
private String getWrapper() {
return params.wrapper;
}
public String getStuffing() {
return params.stuffing;
}
@Override
public String toString() {
return "餃子皮是" + getWrapper() + ",餃子餡是" + getStuffing();
}
/**
* 靜態內部類 Builder
*/
public static class Builder {
Params params = new Params();
public Builder setWrapper(String wrapper) {
params.wrapper = wrapper;
return this;
}
public Builder setStuffing(String stuffing) {
params.stuffing = stuffing;
return this;
}
public Dumpling build() {
Dumpling dumpling = new Dumpling();
params.apply(dumpling.params);
System.out.println("下鍋煮餃子");
return dumpling;
}
}
private static class Params {
private String wrapper; //餃子皮
private String stuffing; //餃子餡
private void apply(Params params){
System.out.println("製作餃子皮:" + wrapper);
params.wrapper = wrapper;
System.out.println("製作餃子陷:" + stuffing);
params.stuffing = stuffing;
}
}
}
複製程式碼
在變種當中,抽象建造者角色沒有了,導演者角色沒有了,只剩下產品角色和具體建造者角色了。
具體建造者變成了產品的私有靜態內部類。
增加了Params靜態內部類,它是用來封裝用到的屬性的。
為什麼要專門封裝這麼一個類呢?
如果沒有的話,則Dumpling會擁有wrapper、stuffing屬性。Builder中也要擁有wrapper、stuffing屬性。
為了屬性的統一性,就增加了Params靜態內部類。
6、用途
建造者模式是一步一步建立一個複雜的物件,它允許使用者只通過指定複雜物件的型別和內容就可以構建它們,使用者不需要知道內部的具體構建細節。
所以,它的作用主要體現在兩方面:複雜物件的構建、忽略具體的構建細節。
大概用途是:
(1)遇到多個構造器引數時。
(2)相同的方法,不同的執行順序,產生不同的事件結果時。
(3)需要生成的產品物件的屬性相互依賴,建造者模式可以強迫生成順序。
7、優點
良好的封裝性。
使用建造者模式可以使客戶端不必知道產品內部的組成細節。
良好的擴充套件性。
增加新的具體建造者無須修改原有類庫的程式碼,指揮者類針對抽象建造者類程式設計,系統擴充套件方便。
更加精細化。
可以更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。
建立不同的產品
將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
使用者使用不同的具體建造者即可得到不同的產品物件。
8、缺點
系統龐大。
如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。
使用侷限性。
建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到一定的限制。
9、END
今天就先說到這裡,下次是工廠模式,感謝大家。