1. 程式人生 > >淺談建造者模式

淺談建造者模式

建造者模式

  建造者模式就是將一個個簡單的物件一步步構建成一個複雜的物件。
  我們生活中有很多可以用建造者模式來解釋。譬如在生產汽車的流水線作業中,我們需要先將生產汽車所需的一個一個的內部構建建造出來,例如發動機,車門,車輪,方向盤,水箱等。對於我們使用者來說,我們並不需要知道這個汽車是怎麼建造出來的,各個部件是怎麼組裝的,銷售人員也不需要知道這個汽車是怎麼組裝建造的,我們只需要知道這是一輛汽車,我們可以銷售和使用就行了,對於銷售人員只需要知道客戶需要什麼樣的汽車,他告訴建造者模式中的指揮者去生產對應的車就可以了,哈哈哈,這都是工業4.0了;同樣,我們看一個簡單的例子,在一家快餐店中,服務員也不需要知道廚師是怎麼炒菜和做飯的,她只需要告訴廚師客戶需要什麼樣的套餐型別就可以了。像這樣,建造者返回給客戶一個完整的的產品物件,而客戶端無須關心該物件所包含的額屬性和組建方式,這就是建造者模式的設計動機。


  定義:建造者模式將一個複雜物件的構建與表示分離,使得同樣的構建過程可以建立不同的表示。
  建造者模式主要包含四個角色:
抽象建造者(Builder): 它宣告為建立一個產品物件的各個部件指定的抽象介面,在該介面中一般宣告兩類方法,一類方法是buildPartX(),它們用於建立複雜物件的各個部件;另一類方法是getResult(),它們用於返回複雜物件。Builder既可以是抽象類,也可以是介面,
具體建造者(ConcreteBuilder): 實現抽象建造者介面,構建和裝配各個部件,定義並明確它所建立的複雜物件,也可以提供一個方法返回建立好的複雜產品物件。
指揮者(Director):
它負責安排複雜物件的建造次序,指揮者與抽象建造者之間存在關聯關係,可以在其construct()建造方法中呼叫建造者物件的部件構造與裝配方法,完成複雜物件的建造。客戶端一般只需要與指揮者進行互動,在客戶端確定具體建造者的型別,並例項化具體建造者物件(也可以通過配置檔案和反射機制) ,然後通過指揮者類的建構函式或者Setter方法將該物件傳入指揮者類中。它主要是用於建立一個複雜的物件,它主要有兩個作用,一是:隔離了客戶與物件的生產過程,二是:負責控制產品物件的生產過程。
產品(Product): 產品角色,一個具體的產品物件。
  我們看下程式碼實現:
第一步,建立一個產品Meal,由兩個元素組成,主食staple和飲料drink。

package builder;

// 產品角色Meal,由兩個元素組成,主食staple和飲料drink
public class Meal {

    private String staple;
    private String drink;

    public String getStaple() {
        return staple;
    }

    public void setStaple(String staple) {
        this.staple = staple;
    }

    public String getDrink() {
        return drink;
    }

    public void setDrink(String drink) {
        this.drink = drink;
    }

    @Override
    public String toString() {
        return "Meal{" +
                "staple='" + staple + '\'' +
                ", drink='" + drink + '\'' +
                '}';
    }
}

第二步,建立一個抽象建造者類MealBuilder,包含建造各個元件的build方法和返回建造物件的getResult。

package builder;

//抽象建造者,包含建造各個元件的build方法和返回建造物件的getResult
public abstract class MealBuilder {

    Meal meal = new Meal();

    //必須是public,用於讓派生的具體建造物件在指揮者類中呼叫
    public Meal getResult() {
        return meal;
    }

    public abstract void buildStaple();
    public abstract void buildDrink();
}

第三步,建立兩個具體的建造者物件ConcreteMealA,ConcreteMealB繼承自MealBuilder,並實現MealBuilder中的抽象方法。

package builder;

//具體的建造物件A,漢堡+可樂
public class ConcreteMealA extends MealBuilder {
    @Override
    public void buildStaple() {
        meal.setStaple("Burger");
    }

    @Override
    public void buildDrink() {
        meal.setDrink("Coca-Cola");
    }
}
package builder;

//具體的建造物件B,pizza+奶茶
public class ConcreteMealB extends MealBuilder {
    @Override
    public void buildStaple() {
        meal.setStaple("Pizza");
    }

    @Override
    public void buildDrink() {
        meal.setDrink("Milk Tea");
    }
}

第四步,建立一個指揮者類Waiter,負責例項化MealBuilder。

package builder;

// 指揮者類,負責建立物件的次序,例項化建立物件
public class Waiter {

    private String name;

    private MealBuilder mealBuilder;

    public void setMealBuilder(MealBuilder mealBuilder) {
        this.mealBuilder = mealBuilder;
    }

    public Meal construct() {
        mealBuilder.buildStaple();
        mealBuilder.buildDrink();
        return mealBuilder.getResult();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

第五步,測試。

package builder;

public class BuilderTest {
    public static void main(String[] args) {
        //叫了一個名為lisa的服務員
        Waiter waiter = new Waiter();
        waiter.setName("Lisa");
        //例項化一個我們所需的A類具體套餐產品
        MealBuilder mealBuilder = new ConcreteMealA();
        //將我們所需A類套餐產品例項傳給服務員物件
        waiter.setMealBuilder(mealBuilder);

        Meal meal = waiter.construct();
        System.out.println("waiter: " + waiter.getName() + " prepares a meal for you, meal is " + meal);
    }
}

第六步,輸出。

waiter: Lisa prepares a meal for you, meal is Meal{staple='Burger', drink='Coca-Cola'}

  總結,建造者模式很像我們在KFC中點餐模式,我們告訴服務員我們需要什麼套餐,服務員根據我們的需求去那對應的套餐給我們,複雜的套餐是怎麼做出來的,我們並不需要知道,在建造者模式中已經幫我們實現了,建造者模式和工廠模式很相似。總體上,建造者模式僅僅只比工廠模式多了一個“導演類”的角色。在建造者模式的類圖中,假如把這個導演類看做是最終呼叫的客戶端,那麼圖中剩餘的部分就可以看作是一個簡單的工廠模式了。但是建造者模式返回一個完整的複雜產品,而抽象工廠模式返回一系列相關的產品;在抽象工廠模式中,客戶端通過選擇具體工廠來生成所需物件,而在建造者模式中,客戶端通過指定具體建造者型別並指導Director類如何去生成物件,側重於一步步構造一個複雜物件,然後將結果返回。如果將抽象工廠模式看成一個汽車配件生產廠,生成不同型別的汽車配件,那麼建造者模式就是一個汽車組裝廠,通過對配件進行組裝返回一輛完整的汽車。
優點:
(1) 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
(2) 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,使用者使用不同的具體建造者即可得到不同的產品物件。由於指揮者類針對抽象建造者程式設計,增加新的具體建造者無須修改原有類庫的程式碼,系統擴充套件方便,符合“開閉原則”
(3) 可以更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。
缺點:
(1) 建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用範圍受到一定的限制。
(2) 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和執行成本。
使用場景:
(1) 需要生成的產品物件有複雜的內部結構,這些產品物件通常包含多個成員屬性。
(2) 需要生成的產品物件的屬性相互依賴,需要指定其生成順序。
(3) 物件的建立過程獨立於建立該物件的類。在建造者模式中通過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中。
(4) 隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品。

優點,缺點,使用場景都是摘自《Java設計模式》一書