1. 程式人生 > 其它 >JAVA設計模式總結—建造者模式

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為抽象建造者,定義了抽象方法

模式效果與應用

建造者模式優點:

  • 客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件

  • 每一個具體建造者都相對獨立,與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,擴充套件方便,符合開閉原則

  • 可以更加精細地控制產品的建立過程

建造者模式缺點:

  • 建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,不適合使用建造者模式,因此其使用範圍受到一定的限制
  • 如果產品的內部變化複雜,可能會需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加了系統的理解難度和執行成本

在以下情況下可以使用建造者模式:

  • 需要生成的產品物件有複雜的內部結構,這些產品物件通常包含多個成員變數

  • 需要生成的產品物件的屬性相互依賴,需要指定其生成順序

  • 物件的建立過程獨立於建立該物件的類。在建造者模式中通過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中

  • 隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品