設計模式之建造者模式

hahadelphi發表於2021-09-09

建造者模式

一:建造者概述

我們大家可能都會開小汽車,但是當你得到一輛小汽車的時候,你可以用它馳騁馬路,但是你知道它元件複雜的構造過程嗎,並不知道。而我們今天要講的建造者模式其實就是複雜的建立型模式客戶端無需知道複雜物件的內部組成和裝配方式,只需要知道建造者的型別即可。它關注的是一步步的建立獨立的複雜物件,不同的具體構造者定義不同的建立過程。其定義如下:

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


二:建造者結構圖

1545290181921

在結構圖中包含以下幾個角色

  • Builder(抽象建造者): 是一個抽象介面,為了建立一個產品物件的各個部件 ,主要有兩類方法,一類是buildXX,用於建立複雜物件的各個部件,一類是getProduct,用於返回複雜物件。
  • ActualBuilder(實際的建造者):實現Builder介面,實現各個部件的建造方法,返回建立好的複雜物件。
  • Product(產品角色):被構建出來的複雜物件,包含多個部件。
  • Director(指揮者):負責安排部件建立的順序,客戶端一般只和指揮者進行互動,在客戶端確定實際的建造者,然後通過指揮者的建構函式或者setter方法將該物件傳入到指揮者類中。

三:典型程式碼

Builder(抽象建造者)

public abstract class Builder {
    // 建立產品物件
    protected  Product product = new Product();

    // 具體部件建造過程在ActualBuilder中實現
    public  abstract void buildPartA();
    public  abstract void buildPartB();
    public  abstract void buildPartC();

    // 定義工廠方法,返回一個完整產品物件
    public Product getProduct(){
        return product;
    }
}
複製程式碼

ActualBuilder(實際的建造者)

public class ActualBuilder extends Builder {
    @Override
    public void buildPartA() {
        product.setPartA("設定部件A");
    }

    @Override
    public void buildPartB() {
        product.setPartA("設定部件B");
    }

    @Override
    public void buildPartC() {
        product.setPartA("設定部件C");
    }
}
複製程式碼

Product(產品角色)

// 產品物件
public class Product {
    // 定義部件
    private String partA;
    private String partB;
    private String partC;

    public String getPartA() {
        return partA;
    }

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public String getPartB() {
        return partB;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    public String getPartC() {
        return partC;
    }

    public void setPartC(String partC) {
        this.partC = partC;
    }
}
複製程式碼

Director(指揮者)

public class Director {
    private  Builder builder;

    // 方式一:通過建構函式設定實際的構造者
    // 傳入型別是基類,面向抽象程式設計,符合里氏替換原則
    public  Director(Builder builder) {
        this.builder=builder;
    }

    // 方式二:通過setter方法設定實際的構造者
    public  void setBuilder(Builder builder) {
        this.builder=builder;
    }

    // 構建複雜產品物件
    public Product construct(){
        // 指揮者可以決定產品部件的構建順序
        builder.buildPartC();
        builder.buildPartA();
        builder.buildPartB();
        return builder.getProduct();
    }
}
複製程式碼

四:組裝小汽車案例

結構圖:

1545292450173

案例程式碼:

產品物件汽車


// 產品物件
public class Car {
    // 定義部件
    // 輪胎
    private String tire;
    // 座椅
    private String seat;
    // 發動機
    private String engine;

    public String getTire() {
        return tire;
    }

    public void setTire(String tire) {
        this.tire = tire;
    }

    public String getSeat() {
        return seat;
    }

    public void setSeat(String seat) {
        this.seat = seat;
    }

    public String getEngine() {
        return engine;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }
}
複製程式碼

汽車的抽象建造者

public abstract class CarBuilder {
    // 建立汽車
    protected Car car = new Car();

    // 建立輪胎
    public  abstract void buildTire();
    // 建立座椅
    public  abstract void buildSeat();
    // 建立發動機
    public  abstract void buildEngine();

    // 定義工廠方法,返回一個完整汽車
    public Car getCar(){
        return car;
    }
}
複製程式碼

具體建造者:大眾

// 大眾汽車
public class DaZhongCarBuilder extends CarBuilder {
    @Override
    public void buildTire() {
        car.setTire("大眾輪胎");
    }

    @Override
    public void buildSeat() {
        car.setSeat("大眾座椅");
    }

    @Override
    public void buildEngine() {
        car.setEngine("大眾發動機");
    }
}
複製程式碼

具體建造者:豐田

// 豐田汽車
public class FenTianCarBuilder extends CarBuilder {

    @Override
    public void buildTire() {
        getCar().setTire("豐田輪胎");
    }

    @Override
    public void buildSeat() {
        getCar().setSeat("豐田座椅");
    }

    @Override
    public void buildEngine() {
        getCar().setEngine("豐田發動機");
    }
}
複製程式碼

指揮者

public class CarDirector {
    private CarBuilder carBuilder;

    // 方式一:通過建構函式設定實際的構造者
    // 傳入型別是基類,面向抽象程式設計,符合里氏替換原則
    public CarDirector(CarBuilder carBuilder) {
        this.carBuilder = carBuilder;
    }

    // 方式二:通過setter方法設定實際的構造者
    public  void setCarBuilder(CarBuilder carBuilder) {
        this.carBuilder = carBuilder;
    }

    // 構建複雜產品物件
    public Car construct(){
        // 指揮者可以決定產品部件的構建順序
        carBuilder.buildTire();
        carBuilder.buildSeat();
        carBuilder.buildEngine();
        return carBuilder.getCar();
    }
}
複製程式碼

客戶端

public class Client {

    public static void main(String[] args) {
        // 建立一個實際車的構造者
        CarBuilder carBuilder = new FenTianCarBuilder();
        // 建立指揮者
        CarDirector carDirector = new CarDirector(carBuilder);
        // 構建出完整產品
        Car product = carDirector.construct();

    }
}
複製程式碼

說明整個流程其實很簡單,指揮者指導構建者一步步的構造完整產品,根據指揮者定義的構造過程可以建立出完全不同的產品


五:如何控制構造者不生產某個部件

我們發現構造整個產品的構造過程都是按照指揮者構建部件的順序逐步構建,但是可能有的具體的構造者不需要某個部件,比如大眾汽車不需要座椅(當然這是不可能的),那麼它就不需要呼叫buildSeat()方法。為了解決這個問題,我們引入一個鉤子方法,通常鉤子方法名為isXXX(),其定義在抽象構造者類中,我們可以定義一個isSeat()的方法,來判斷是否需要座椅,併為之提供一個預設實現為false。

抽象構造者中設定是否需要座椅的鉤子方法

public abstract class CarBuilder {
    // 建立汽車
    protected Car car = new Car();

    // 建立輪胎
    public  abstract void buildTire();
    // 建立座椅
    public  abstract void buildSeat();
    // 建立發動機
    public  abstract void buildEngine();

    // 定義一個鉤子方法,是否需要座椅, 預設為true
    public boolean isSeat(){
        return  true;
    }

    // 定義工廠方法,返回一個完整汽車
    public Car getCar(){
        return car;
    }
}
複製程式碼

具體構造者覆蓋鉤子方法,返回false,表示不需要座椅

// 大眾汽車
public class DaZhongCarBuilder extends CarBuilder {
    @Override
    public void buildTire() {
        car.setTire("大眾輪胎");
    }

    @Override
    public void buildSeat() {
        car.setSeat("大眾座椅");
    }

    @Override
    public void buildEngine() {
        car.setEngine("大眾發動機");
    }

    @Override
    public boolean isSeat() {
        return false;
    }
}
複製程式碼

指揮者根據具體構造者的需求構建產品

public class CarDirector {
    private CarBuilder carBuilder;
	// ........省略部分程式碼

    // 構建複雜產品物件
    public Car construct(){
        // 指揮者可以決定產品部件的構建順序
        carBuilder.buildTire();
       	// 鉤子方法 用來確定是否需要構建某個部件
        if(carBuilder.isSeat()){
            // 表示需要座椅的時候才會構建
            carBuilder.buildSeat();
        }
        carBuilder.buildEngine();
        return carBuilder.getCar();
    }
}
複製程式碼

六:建造者模式總結

  • 優點

    1. 客戶端不需要知道具體建立物件的細節,將產品本身和產品的建立過程解耦,相同的建立過程可以建立出不同的產品物件。
    2. 每個具體建造者相對獨立,增加新的具體建造者不會影響現有的類庫程式碼,符合開閉原則。
    3. 可以採用鉤子方法精確的控制某個具體建造者是否需要某個部件。可以針對的控制產品構建流程。
  • 缺點

    1. 如果產品之間的差異性較大,那麼即使使用鉤子方法來控制,那也是極其麻煩,且公共介面很難抽象。非常不適合。
  • 適用場景

    1. 需要生成的產品物件有複雜的內部結構,這些產品物件通常包含多個成員屬性。
    2. 需要生成的產品物件的屬性相互依賴,需要指定其生成順序。
    3. 物件的建立過程獨立於建立該物件的類。在建造者模式中通過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中。
    4. 隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品。

七:抽象工廠模式和建造者模式的區別

  1. 抽象工廠模式返回的是一系列相關的產品,而建造者模式返回的是一個具體的完整物件。
  2. 抽象工廠模式的客戶端是通過選擇具體工廠來生成所需要的物件,而建造者模式是通過Director類去指導如何一步步的生成部件,返回完整物件。
  3. 簡單的理解:抽象工廠模式就是汽車配件生產廠,生產不同型別的汽車配件,而建造者模式就是一個汽車組裝廠,組裝配件返回完整汽車。

參考:blog.csdn.net/lovelion/ar…


號外號外:

  • 如果有小夥伴覺得我寫的不錯的話可以關注一下我的部落格哦
  • 可以關注下方我的公眾號**java架構師小密圈,回覆1**:獲取2Tjava架構師必備乾貨另外:小夥伴可以回覆任意想學的技術,可以免費幫你搜尋其實我們還需要學很多!!!!!!

1545271756941

  • 還會分享一些賺錢理財的小套路哦,歡迎大家來支援,一起學習成長,程式設計師不僅僅是搬瓦工!

公眾號:分享系列好文章

java架構師小密圈

交流群:一起奔著java架構師努力

java架構師小密圈

相關文章