JAVA設計模式總結—建造者模式
建造者模式
模式動機與定義
首先建造者模式的動機是為了建立複雜物件,簡化傳統的建立方法,提高建立的效率和可讀性。
像圖中的這個例子,使用者的需求是駕駛一輛汽車,但是對於使用者來說是不需要了解汽車裝配廠汽車的具體裝配過程,使用者只是使用汽車整個整體。
所以在這個例子中,使用者只需要知道我要型別為汽車的物件就夠了,而不需要知道這個複雜物件到底是怎麼組成的,我理解的就是, 建造者模式就是提供一個介面,實現接受使用者一個汽車型別的引數,然後通過內部建造,返回一個汽車物件。
模式結構與分析
模式定義
-
建造者模式(Builder Pattern):將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
-
建造者模式是一步一步建立一個複雜的物件,它允許使用者只通過指定複雜物件的型別和內容就可以構建它們,使用者不需要知道內部的具體構建細節。建造者模式屬於物件建立型模式。
構建過程:汽車的組裝過程
不同的表示: 構建不同的汽車,比如有轎車,電動汽車,運動型汽車比如,汽車有很多組成部分,但是使用者不需要每個組成部分都要指定,使用者可以通過指定汽車的一些特點,比如電動車,自動擋這些特徵,就可以建立一輛車的物件, 但是具體車是怎麼組裝的,什麼先後順序,其他沒有指定的部分的特徵,比如車的高度就不需要知道。
- 將客戶端與包含多個部件的複雜物件的建立過程分離,客戶端無須知道複雜物件的內部組成部分與裝配方式,只需要知道所需建造者的型別即可
- 關注如何逐步建立一個複雜的物件,不同的建造者定義了不同的建立過程
模式結構
- Product: 最終要生成的物件,例如 Computer例項。
- Builder: 構建者的抽象基類(有時會使用介面代替)。其定義了構建Product的抽象步驟,其實體類需要實現這些步驟。其會包含一個用來返回最終產品的方法
Product getProduct()
。 - ConcreteBuilder: Builder的實現類。
- Director: 決定如何構建最終產品的演算法. 其會包含一個負責組裝的方法
void Construct(Builder builder)
, 在這個方法中通過呼叫builder的方法,就可以設定builder,等設定完成後,就可以通過builder的getProduct()
時序圖
模式例項與解析
比如有一個老闆現在需要造一個高樓,這個老闆在建造者模式就是一個客戶類(client)
,呼叫Director
的建造方法,然後建造者模式中的指揮者類(Director)
就相當於是一個包工頭
,他把建造的工作分配給底下的工人,而不同的工人可以建造的房屋型別不同,就比如有些工人只會建造普通的屋子,有些工人可以建造高樓這些工人
就是具體的建造者(ConcreteBuilder)
,指揮者就通過客戶端的需求給具體的工人分配工作,建造不同的房屋,房屋就是建造者模式裡的產品類(product)
參考類圖
相比之前的標準建造者類圖,該類圖多了一個Client客戶類,客戶類依賴指揮者類和具體的建造者類,具體的建造者和product是依賴關係,具體的建造者繼承抽象建造者,指揮者和抽象建造是聚合關係。
參考程式碼
Product:產品,對應例子中的房子
public class House {
// 地基
private String baise;
// 牆面
private String wall;
// 屋頂
private String roofed;
@Override
public String toString() {
return "House{" +
"baise='" + baise + '\'' +
", wall='" + wall + '\'' +
", roofed='" + roofed + '\'' +
'}';
}
/**
* get set方法
*/
}
Builder: 抽象的建造者,對應例子中的工人
public abstract class HouseBuilder {
protected House house = new House();
// 將建造的流程寫好抽象的方法
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void buildRoofed();
// 房子建造好,將房子返回
public House buildHouse() {
System.out.println(house);
return house;
}
}
ConcreteBuilder: 具體的建造者,對應例子中的具體工人
// 蓋普通房子的工人
public class CommonHouse extends HouseBuilder{
@Override
public void buildBasic() {
System.out.println("普通房子打地基5m");
house.setBaise("5m");
}
@Override
public void buildWalls() {
System.out.println("普通房子砌牆10cm");
house.setWall("10cm");
}
@Override
public void buildRoofed() {
System.out.println("普通房子屋頂");
house.setRoofed("common");
}
}
// 蓋高樓的工人
public class HighBuilding extends HouseBuilder{
@Override
public void buildBasic() {
System.out.println("高房打地基10m");
house.setBaise("10m");
}
@Override
public void buildWalls() {
System.out.println("高樓砌牆20cm");
house.setWall("20cm");
}
@Override
public void buildRoofed() {
System.out.println("高樓屋頂");
house.setRoofed("high");
}
}
Director: 指揮者類,這裡對應例子中的包工頭
// 指揮者,這裡去指定製作流程,返回產品
public class HouseDirector {
HouseBuilder houseBuilder = null;
// 構造器傳入houseBuilder
public HouseDirector(HouseBuilder houseBuilder){
this.houseBuilder = houseBuilder;
}
// 通過set傳入houseBuilder
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
// 指揮HouseBuilder如何建造房子,建造房子的流程
public House constructHouse() {
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.buildRoofed();
return houseBuilder.buildHouse();
}
}
Client: 客戶類,這裡對應例子中的老闆
public class Client {
public static void main(String[] args) {
// 蓋普通房子
CommonHouse commonHouse = new CommonHouse();
// 準備蓋普通房子的指揮者
HouseDirector houseDirector = new HouseDirector(commonHouse);
// 完成蓋房子,返回產品
House house = houseDirector.constructHouse();
}
}
執行結果:
普通房子打地基5m
普通房子砌牆10cm
普通房子屋頂
House{baise='5m', wall='10cm', roofed='common'}
建造者模式在JDK中的應用和原始碼分析
StringBuilder
下面的例子是StringBuilder
的簡單應用
public class Builder {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("hello,");
stringBuilder.append("world");
System.out.println(stringBuilder);
}
}
現在來看一下StringBuilder
的定義
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence{
...
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
...
}
可以看到StringBuilder在呼叫append方法後,是使用了super的方法進行呼叫,StringBuilder在此充當了指揮者角色,同時充當了具體的建造者,建造方法的實現是由AbstractStringBuilder完成,而StringBuilder繼承了AbstractStringBuilder,所以接下來我們再來看看AbstractStringBuilder
的定義
abstract class AbstractStringBuilder implements Appendable, CharSequence {
...
public AbstractStringBuilder append(StringBuffer sb) {
return this.append((AbstractStringBuilder)sb);
}
...
}
AbstractStringBuilder 實現了 Appendable介面方法,這裡的AbstractStringBuilder 已經是建造者,只是不能例項化
public interface Appendable {
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(char c) throws IOException;
}
Appendable介面定義了多個append方法(抽象方法),即Appendable為抽象建造者,定義了抽象方法
模式效果與應用
建造者模式優點:
-
客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件
-
每一個具體建造者都相對獨立,與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,擴充套件方便,符合開閉原則
-
可以更加精細地控制產品的建立過程
建造者模式缺點:
- 建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,不適合使用建造者模式,因此其使用範圍受到一定的限制
- 如果產品的內部變化複雜,可能會需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加了系統的理解難度和執行成本
在以下情況下可以使用建造者模式:
-
需要生成的產品物件有複雜的內部結構,這些產品物件通常包含多個成員變數
-
需要生成的產品物件的屬性相互依賴,需要指定其生成順序
-
物件的建立過程獨立於建立該物件的類。在建造者模式中通過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中
-
隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品