0、背景
建造模式(Builder模式)
假如有一個需求:蓋房子,蓋房子過程是一致的:打樁、砌牆、封頂。但是房子是各式各樣的,最後蓋出來的房子可能是高樓或別墅。
根據直接的思路,不用設計模式思想,我們也許會:
- 寫一個 CommonHouse 抽象類,然後裡面規定了打樁、砌牆、封頂三個方法;
- 讓不同房子繼承這個類;
- 最後呼叫的時候呼叫分別的方法即可。
在繼承抽象類、子類區分這一塊,思想沒有問題,問題出現這些類本身。
缺點:
過於簡單,將產品(房子)和建立產品(房子建造流程)封裝在了一起,耦合性增強了。(可以理解為,物件導向的思想裡,房子雖然是一個類,擁有自己的方法,但是房子不應該擁有建造自己的方法)
解決方法:
解耦 - > 建造者模式。
一、建造者模式
建造者模式(Builder Pattern)又叫生成器模式,是一種物件構建模式(建立型),可以將複雜物件的建造過程抽象出來(抽象類),使這個抽象過程的不同實現方法能構造出不同表現(屬性)的物件。
建造模式允許使用者只通過指定複雜物件的型別和內容就可以構建他們,使用者不需要知道內部的具體構建細節。
建造者模式的四個角色:
- Product產品:一個具體產品物件;
- Builder(抽象建造者):建立Product物件指定的 介面或者抽象類;
- ConcreteBuilder具體建造者:實現介面,構建和裝配各個部件;
- Director指揮者:構建一個使用Builder介面的物件,主要用於建立一個複雜度物件,有兩個作用:一是隔離客戶與物件的生產過程,二是負責控制產品物件的生產過程。
他們之間的關係,我們用類圖來解釋:
- Directer裡面聚合一個Builder實際上使用的是他的實現類ConcreteBuilder;
- ConcreteBuilder可以有很多,就是所謂的不同的房子的建造者。
因為getRusult是一樣的,所以暫時不用介面,用抽象類實現Builder,程式碼如下:
/*
產品,對應product
*/
public class House {
private String base;
private String wall;
private String roof;
//對應getset方法
}
/*
抽象的建造者,對應Builder
*/
public abstract class HouseBuilder {
protected House house = new House();
//寫好流程的各個方法,但不約束具體執行
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void buildRoof();
//建造方法,返回建造結果
public House buildHouse(){
return house;
}
}
/*
普通房子製造流程,繼承抽象類
可以看到,製造流程在這裡,而House類擁有房子的屬性,他們是分開的
應是對House的操作,這裡省略
*/
public class CommonHouse extends HouseBuilder{
@Override
public void buildBasic() {
System.out.println("普通房子:建造地基。。。");
}
@Override
public void buildWalls() {
System.out.println("普通房子:砌牆。。。");
}
@Override
public void buildRoof() {
System.out.println("普通房子:蓋屋頂。。。");
}
}
/*
另一個實現類,本來應是對House的操作,這裡省略
*/
public class HighHouse extends HouseBuilder {
@Override
public void buildBasic() {
System.out.println("高樓:建造地基。。。");
}
@Override
public void buildWalls() {
System.out.println("高樓:砌牆。。。");
}
@Override
public void buildRoof() {
System.out.println("高樓:蓋屋頂。。。");
}
}
/*
Director,聚合建造者HouseBuilder
同時決定製作流程,最後呼叫建造者的buildHouse方法返回
*/
public class Director {
HouseBuilder houseBuilder = null;
//通過構造器聚合
public Director(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
//通過setter方法聚合
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
//指揮具體建造流程,先後順序不由Builder決定
public House constructHouse(){
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.buildRoof();
return houseBuilder.buildHouse();
}
}
/*
客戶端
*/
public class Client {
public static void main(String[] args) {
//new房子
CommonHouse commonHouse = new CommonHouse();
//new指揮者
Director director = new Director(commonHouse);
//完成蓋房
House house = director.constructHouse();
//重置建造者
HighHouse highHouse = new HighHouse();
director.setHouseBuilder(highHouse);
House house1 = director.constructHouse();
}
}
上面程式碼總結起來:一是要把房子歸為房子,有屬性就夠了,建造歸建造,相當於是建築工人,和房子兩個物件分開,那麼Director相當於包工頭,對於不同的House要指揮不一樣的Builder群體。
二、建造者模式在JDK中的應用
java.lang.StringBuilder類,也就是常用的可變字串類,用到的就是建造者模式。以其中的常用方法 append 方法為例看原始碼。
2.1 Appendable 介面
定義了多個 append 方法(抽象方法),即 Appendable 就是我們的抽象建造者Builder 。
2.2 具體的實現類 AbstractStringBuilder
雖然也是一個抽象類,但是也已經實現了 Append able 介面的方法,所以其實相當於是具體的建造者ConcreteBuilder了;
2.3 StringBuilder,繼承了AbstractStringBuilder
但是他重寫 append 方法的方式只是呼叫了父類方法,所以應該說,StringBuilder既充當了Director,又是一個ConcreteBuilder.
三、注意事項
- 客戶端不必知道產品呢內部組成的細節,將產品本身和產品的建立過程解耦,使得相同的建立過程可以建立不同的產品;
- 每一個具體的建造者都是相對獨立的,和其他的建造者無關,因此可以方便替換具體建造者或者增加新的具體建造者,使用者使用不同的具體建造者就能得到不同產品;
- 可以更加精細的控制建立過程;
- 增加新的具體建造者無需修改原有程式碼,指揮者針對抽象建造者程式設計;
- 建造者模式建立的產品,一定是共同點比較多,組成部分相似,如果產品之間的差異性很大,那麼就不適合使用建造者模式,因此其適用範圍受到一定的限制。
建造者模式 VS 抽象工廠模式:
抽象工廠模式實現對產品家族的建立,一個產品家族:具有不同分類維度的產品組合,採用抽象工廠模式不需要關心構建過程,只關心什麼產品由什麼工廠生產。
建造者模式是按照指定的要求創造產品,主要目的是通過組裝零件產生一個新產品。
(可能有點模糊,但是個人認為本來就是模糊的。。只是一種設計思想,沒有嚴格的邊界。。。)
多多理解,就會清楚的!