【設計模式】第四篇:建造者模式也沒那麼難

BWH_Steven發表於2020-11-09

一 引言

說明:如果想要直接閱讀定義等理論內容,可以直接跳轉到第二大點

在生活中有很多場景與我們今天要說的 “建造者模式” 是非常匹配的,打個比方一臺計算機是由 CPU、記憶體、顯示卡、記憶體、滑鼠、鍵盤、顯示器等等內容組合而成的,我們想要一臺電腦,我們不會可能自己去做這些配件,一般都是通過告訴銷售公司,然後其派生產技術人員給你做好指定的配件。

先不管,誰買,誰做,誰管理的問題,我們可以分析得到,建造電腦的這個 “過程” 是穩定的也就是說,不管什麼配置的電腦,這些配件都是必須要有的,只是具體的細節不一樣,例如你的配置更好,他的差一些

但是,我作為一個買家,我並不想管這些,我只告訴你,我要一臺中等配置的電腦,你負責“建造”好給我就行了,這就是建造者模式比較通俗的講法

下面我們通過這個計算機的例子,循序漸進的看一下:

二 通過例子循序漸進認識建造者模式

首先,不管怎麼建,怎麼買,一個 Computer 電腦類是必須的,我們隨便挑選三個元件來進行演示,CPU、記憶體、顯示器,補充其 get set toString 方法

/**
 * 產品:電腦
 */
public class Computer {
    private String cpu; // CPU
    private String graphicsCard; // 記憶體
    private String displayScreen; // 顯示器

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getGraphicsCard() {
        return graphicsCard;
    }

    public void setGraphicsCard(String graphicsCard) {
        this.graphicsCard = graphicsCard;
    }

    public String getDisplayScreen() {
        return displayScreen;
    }

    public void setDisplayScreen(String displayScreen) {
        this.displayScreen = displayScreen;
    }
    
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", graphicsCard='" + graphicsCard + '\'' +
                ", displayScreen='" + displayScreen + '\'' +
                '}';
    }
}

(一) 最簡單直白的方式(不好)

這種方法基本可以說沒什麼技術含量了,直接 new + set 就行了

public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setCpu("英特爾酷睿 i5 處理器");
        computer.setGraphicsCard("4g記憶體");
        computer.setDisplayScreen("14寸 1080p 60hz顯示器");
        System.out.println(computer.toString());
    }
}

列印一下結果:

Computer{cpu='英特爾酷睿 i5 處理器', graphicsCard='4g', displayScreen='14寸 1080p 60hz顯示器'}

(二) 客戶直接聯絡生產技術人員

上面這種方式,不用說也知道不合適了,所以技術人員(建造者)他來了!但是不同的技術人員會製作的配件也不一樣,例如有的會做 144hz 的顯示器,而有的專攻 60hz 的顯示器,有高低配置,不同型號之分

為此我們為其抽象出一個 ComputerBuilder 的抽象類

/**
 * 電腦的建造者
 */
public abstract class ComputerBuilder {
    abstract void buildCpu(); // 建造CPU
    abstract void buildGraphicsCard(); // 建造記憶體
    abstract void buildDisplayScreen(); // 建造顯示器

    abstract Computer getComputer(); // 拿到這臺電腦
}

下面就來寫建造者的具體實現,例如先寫一個低配置電腦建造的實現

/**
 * 低配置電腦
 */
public class LowConfigurationComputerBuilder extends ComputerBuilder {

    private Computer computer;

    public LowConfigurationComputerBuilder(){
        computer = new Computer();
    }

    @Override
    void buildCpu() {
        computer.setCpu("英特爾酷睿 i5 處理器");
        System.out.println("buildCpu: 英特爾酷睿 i5 處理器");
    }

    @Override
    void buildGraphicsCard() {
        computer.setGraphicsCard("8g記憶體");
        System.out.println("buildGraphicsCard: 8g記憶體");
    }

    @Override
    void buildDisplayScreen() {
        computer.setDisplayScreen("1080p 60hz顯示器");
        System.out.println("buildDisplayScreen: 1080p 60hz顯示器");
    }

    @Override
    Computer getComputer() {
        return computer;
    }
}

測試一下:

public class Test {
    public static void main(String[] args) {
        // 建立低配置電腦建造者
        LowConfigurationComputerBuilder builder = new LowConfigurationComputerBuilder();
        builder.buildCpu();
        builder.buildGraphicsCard();
        builder.buildDisplayScreen();
        Computer computer = builder.getComputer();
        System.out.println("建造出的電腦: " + computer );
    }
}

執行結果:

buildCpu: 英特爾酷睿 i5 處理器
buildGraphicsCard: 8g記憶體
buildDisplayScreen: 1080p 60hz顯示器
建造出的電腦: Computer{cpu='英特爾酷睿 i5 處理器', graphicsCard='8g記憶體', displayScreen='1080p 60hz顯示器'}

(三) 客戶聯絡銷售公司

雖然上面的方法是比第一種強一些,但是客戶自己去聯絡生產技術人員,顯然不是很合理,正常的做法,我們都是先去聯絡銷售公司,告訴他們我想要什麼配置的電腦就可以了,細節我並不想管

public class SalesCompany {
    public Computer buildComputer(ComputerBuilder builder){
        builder.buildCpu();
        builder.buildGraphicsCard();
        builder.buildDisplayScreen();
        return builder.getComputer();
    }
}

測試程式碼

public class Test {
    public static void main(String[] args) {
        // 建立低配置電腦建造者
        LowConfigurationComputerBuilder builder = new LowConfigurationComputerBuilder();
        // 建立電腦銷售中心
        SalesCompany salesCompany = new SalesCompany();
        // 指定具體的電腦建造者去完成 電腦 這個產品
        Computer computer = salesCompany.buildComputer(builder);
        System.out.println("建造出的電腦: " + computer );
    }
}

現在程式碼已經比較完善了,也就是我們買家通過聯絡電腦銷售中心,然後銷售中心去呼叫使用者想要的配置的建造者,剛才我們建立的是一個“低配置電腦建造者”,如果我們想要換成中等配置的電腦,該怎麼做呢?

現在只需要增加一箇中等配置電腦建造者,實現Builder抽象類就可以了

/**
 * 低配置電腦
 */
public class MiddleConfigurationComputerBuilder extends ComputerBuilder {

    private Computer computer;

    public MiddleConfigurationComputerBuilder(){
        computer = new Computer();
    }

    @Override
    void buildCpu() {
        computer.setCpu("英特爾酷睿 i7 處理器");
        System.out.println("buildCpu: 英特爾酷睿 i7 處理器");
    }

    @Override
    void buildGraphicsCard() {
        computer.setGraphicsCard("16g記憶體");
        System.out.println("buildGraphicsCard: 16g記憶體");
    }

    @Override
    void buildDisplayScreen() {
        computer.setDisplayScreen("2k 144hz顯示器");
        System.out.println("buildDisplayScreen: 2k 60hz顯示器");
    }

    @Override
    Computer getComputer() {
        return computer;
    }
}

測試一下

public class Test {
    public static void main(String[] args) {
        MiddleConfigurationComputerBuilder builder = new MiddleConfigurationComputerBuilder();
        // 建立電腦銷售中心
        SalesCompany salesCompany = new SalesCompany();
        // 指定具體的電腦建造者去完成 電腦 這個產品
        Computer computer = salesCompany.buildComputer(builder);
        System.out.println("建造出的電腦: " + computer );
    }
}

執行結果

buildCpu: 英特爾酷睿 i7 處理器
buildGraphicsCard: 16g記憶體
buildDisplayScreen: 2k 60hz顯示器
建造出的電腦: Computer{cpu='英特爾酷睿 i7 處理器', graphicsCard='16g記憶體', displayScreen='2k 144hz顯示器'}

其實到這裡一個建造者模式的例項就寫完了,下面我們結合概念,來深入理解一下建造者模式

三 建造者模式

(一) 概念

建造者模式:將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示

  • 也就是說,產品的生成過程或者說組成,是不變的,而每一部分都是可以自行選擇的,即其內部表象是可以變化的,這也就是所說的變與不變相分離

  • 此種情況下,使用者只需要指定建造的型別就可以得到他們,而具體的過程和細節就不需要知道了

(二) 優缺點

首先,此模式封裝性好,構建和表示進行了分離,每一個建造者都是相互獨立的,利於解耦和擴充套件,符合 “開閉原則” 同時客戶呼叫時,不需要知道產品細節

但是也正是因為產品生成過程這個不變的部分,限制了它的使用範圍,同時如果產品內部日後發生什麼改變,則建造者也得同樣修改,維護成本不小

(三) 結構

根據上面的結構圖,我們分別說明一下其中的四個角色(除 Client 呼叫者以外)

  • Product(產品角色):多個元件構成的複雜物件,即上述例子中的電腦
  • Builder(抽象建造者):一個包含建立產品各個子部件的抽象方法的介面/抽象類,一般還包含一個返回結果的方法,如上述中的 ComputerBuilder 類
  • ConcreteBuilder(具體建造者):Builder 的具體實現類,如上述中具體的 低配置電腦建造者 和 中等配置電腦建造者
  • Director(指揮者):呼叫建造者物件中的部件構造與裝配方法,以建立一個複雜物件
    • 指揮者中不含具體產品資訊
    • 其隔離了客戶與物件的生產過程

(四) 適用場景

  • 順序會對同一方法的結果產生影響,例如建房子,應當先打地基,再架鋼筋水泥
  • 同一個物件可以裝配不同的部件或者零件,同時結果不同
  • 產品類有複雜的內部結構,且這些產品物件具有共性

相關文章