設計模式干貨系列:(六)建造者模式【學習難度:★★★★☆,使用頻率:★★☆☆☆】

前言

今天介紹建造者模式,建造者模式又稱為生成器模式,它是一種較為復雜、使用頻率也相對較低的創建型模式。建造者模式為客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的復雜產品。舉個簡單的例子,比如我們去買電腦,買的是成品而不是散裝的零件。

正文

建造者模式概念

建造者模式(Builder Pattern):將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種對象創建型模式。

產品的內部表象

一個產品常有不同的組成成分作為產品的零件,這些零件有可能是對象,也有可能不是對象,它們通常又叫做產品的內部表象(internal representation)。不同的產品可以有不同的內部表象,也就是不同的零件。使用建造模式可以使客戶端不需要知道所生成的產品有哪些零件,每個產品的對應零件彼此有何不同,是怎么建造出來的,以及怎么組成產品。

對象性質的建造

有些情況下,一個對象會有一些重要的性質,在它們沒有恰當的值之前,對象不能作為一個完整的產品使用。比如,一個電子郵件有發件人地址、收件人地址、主題、內容、附錄等部分,而在最起碼的收件人地址得到賦值之前,這個電子郵件不能發送。

有些情況下,一個對象的一些性質必須按照某個順序賦值才有意義。在某個性質沒有賦值之前,另一個性質則無法賦值。這些情況使得性質本身的建造涉及到復雜的商業邏輯。這時候,此對象相當于一個有待建造的產品,而對象的這些性質相當于產品的零件,建造產品的過程是建造零件的過程。由于建造零件的過程很復雜,因此,這些零件的建造過程往往被“外部化”到另一個稱做建造者的對象里,建造者對象返還給客戶端的是一個全部零件都建造完畢的產品對象。

建造模式利用一個導演者對象和具體建造者對象一個個地建造出所有的零件,從而建造出完整的產品對象。建造者模式將產品的結構和產品的零件的建造過程對客戶端隱藏起來,把對建造過程進行指揮的責任和具體建造者零件的責任分割開來,達到責任劃分和封裝的目的。

建造者模式結構圖

在建造者模式結構圖中包含如下幾個角色:

● Builder(抽象建造者):它為創建一個產品Product對象的各個部件指定抽象接口,在該接口中一般聲明兩類方法,一類方法是buildPartX(),它們用于創建復雜對象的各個部件;另一類方法是getResult(),它們用于返回復雜對象。Builder既可以是抽象類,也可以是接口。

●ConcreteBuilder(具體建造者):它實現了Builder接口,實現各個部件的具體構造和裝配方法,定義并明確它所創建的復雜對象,也可以提供一個方法返回創建好的復雜產品對象。

●Product(產品角色):它是被構建的復雜對象,包含多個組成部件,具體建造者創建該產品的內部表示并定義它的裝配過程。

● Director(指揮者):指揮者又稱為導演類,它負責安排復雜對象的建造次序,指揮者與抽象建造者之間存在關聯關系,可以在其construct()建造方法中調用建造者對象的部件構造與裝配方法,完成復雜對象的建造。客戶端一般只需要與指揮者進行交互,在客戶端確定具體建造者的類型,并實例化具體建造者對象(也可以通過配置文件和反射機制),然后通過指揮者類的構造函數或者Setter方法將該對象傳入指揮者類中。

導演者角色是與客戶端打交道的角色。導演者將客戶端創建產品的請求劃分為對各個零件的建造請求,再將這些請求委派給具體建造者角色。具體建造者角色是做具體建造工作的,但是卻不為客戶端所知。

一般來說,每有一個產品類,就有一個相應的具體建造者類。這些產品應當有一樣數目的零件,而每有一個零件就相應地在所有的建造者角色里有一個建造方法。這里就舉買電腦的例子來介紹,我們假設電腦需要2個組件,主機和顯示器。

代碼示例

下面是Computer電腦產品的源代碼:

public class Computer {
    private String host;//主機
    private String display;// 顯示屏

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getDisplay() {
        return display;
    }

    public void setDisplay(String display) {
        this.display = display;
    }
}

下面是抽象建造者類Builder的源代碼:

public interface  Builder {
    public void buildHost();
    public void buildDisplay();
    public Computer retrieveResult();
}

下面是具體建造者類LenovoBuilder的源代碼:

public class LenovoBuilder implements Builder {
    Computer computer = new Computer();

    /**
     * 產品零件建造方法1
     */
    @Override
    public void buildDisplay() {
        //構建產品的第一個零件
        computer.setDisplay("聯想顯示器");
    }

    /**
     * 產品零件建造方法1
     */
    @Override
    public void buildHost() {
        //構建產品的第二個零件
        computer.setHost("聯想主機");
    }

    /**
     * 產品返還方法
     */
    @Override
    public Computer retrieveResult() {
        return computer;
    }

}

下面是具體建造者類HuaweiBuilder的源代碼:

public class HuaweiBuilder implements  Builder{
    Computer computer = new Computer();

    /**
     * 產品零件建造方法1
     */
    @Override
    public void buildDisplay() {
        //構建產品的第一個零件
        computer.setDisplay("華為顯示器");
    }

    /**
     * 產品零件建造方法1
     */
    @Override
    public void buildHost() {
        //構建產品的第二個零件
        computer.setHost("華為主機");
    }
    /**
     * 產品返還方法
     */
    @Override
    public Computer retrieveResult() {
        return computer;
    }
}

下面是指揮者類Director類的源代碼:

public class Director {
    /**
     * 持有當前需要使用的建造器對象
     */
    private Builder builder;
    /**
     * 構造方法,傳入建造器對象
     * @param builder 建造器對象
     */
    public Director(Builder builder){
        this.builder = builder;
    }
    /**
     * 產品構造方法,負責調用各個零件建造方法
     */
    public void construct(){
        builder.buildHost();
        builder.buildDisplay();
    }
}

下面是客戶端類Client源代碼:

public class Client {
    public static void main(String[]args){
        Builder builder = new LenovoBuilder();
        Director director = new Director(builder);
        director.construct();
        Computer computer = builder.retrieveResult();
        System.out.println(computer.getHost());
        System.out.println(computer.getDisplay());
    }
}

輸出:

聯想主機
聯想顯示器

以上代碼就是模式了建造者模式的使用場景,其中電腦銷售人員相當于指揮者,只要客戶確定電腦的類型,電腦銷售人員可以通知電腦組裝人員給客戶組裝一臺電腦。

我們來看看時序圖:

客戶端負責創建導演者和具體建造者對象。然后,客戶端把具體建造者對象交給導演者,導演者操作具體建造者,開始創建產品。當產品完成后,建造者把產品返還給客戶端。

把創建具體建造者對象的任務交給客戶端而不是導演者對象,是為了將導演者對象與具體建造者對象的耦合變成動態的,從而使導演者對象可以操縱數個具體建造者對象中的任何一個。

總結

建造者模式的核心在于如何一步步構建一個包含多個組成部件的完整對象,使用相同的構建過程構建不同的產品,在軟件開發中,如果我們需要創建復雜對象并希望系統具備很好的靈活性和可擴展性可以考慮使用建造者模式。

1.主要優點

  • 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。

  • 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產品對象。由于指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統擴展方便,符合“開閉原則”

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

2.主要缺點

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

  • 如果產品的內部變化復雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和運行成本。

3.適用場景

  • 需要生成的產品對象有復雜的內部結構,這些產品對象通常包含多個成員屬性。

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

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

  • 隔離復雜對象的創建和使用,并使得相同的創建過程可以創建不同的產品。


一直覺得自己寫的不是技術,而是情懷,一篇篇文章是自己這一路走來的痕跡。靠專業技能的成功是最具可復制性的,希望我的這條路能讓你少走彎路,希望我能幫你抹去知識的蒙塵,希望我能幫你理清知識的脈絡,希望未來技術之巔上有你也有我。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容