1. 程式人生 > >Java 設計模式(六):建造者模式

Java 設計模式(六):建造者模式

參考連結:建造者模式-Builder Pattern

1. 模式概述

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

建造者模式是較為複雜的建立型模式,它將客戶端與包含多個組成部分(或部件)的複雜物件的建立過程分離,客戶端無須知道複雜物件的內部組成部分與裝配方式,只需要知道所需建造者的型別即可。它關注如何一步一步建立一個的複雜物件,不同的具體建造者定義了不同的建立過程,且具體建造者相互獨立,增加新的建造者非常方便,無須修改已有程式碼,系統具有較好的擴充套件性。

建造者模式結構如圖所示:
在這裡插入圖片描述

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

  • Builder(抽象建造者):它為建立一個產品Product物件的各個部件指定抽象介面,在該介面中一般宣告兩類方法,一類方法是buildX(),用於建立複雜物件的各個部件;另一類方法是getResult(),用於返回複雜物件。Builder既可以是抽象類,也可以是介面。
  • ConcreteBuilder(具體建造者):實現Builder介面,負責各個部件的具體構造和裝配方法。
  • Product(產品角色):它是被構建的複雜物件,包含多個組成部件,具體建造者建立該產品的內部表示並定義它的裝配過程。
  • Director(指揮者):指揮者又稱為導演類,它負責安排複雜物件的建造次序,指揮者與抽象建造者之間存在關聯關係,可以在其construct()建造方法中呼叫建造者物件的部件構造與裝配方法,完成複雜物件的建造。客戶端一般只需要與指揮者進行互動,在客戶端確定具體建造者的型別,並例項化具體建造者物件(也可以通過配置檔案和反射機制),然後通過指揮者類的建構函式或者Setter方法將該物件傳入指揮者類中。

在建造者模式的定義中提到了複雜物件,那麼什麼是複雜物件?簡單來說,複雜物件是指那些包含多個成員屬性的物件,這些成員屬性也稱為部件或零件。以點餐為例,食物包含漢堡、飲料以及它們的包裝等。

定義一個表示食物條目和食物包裝的介面。

public interface Item {
    String name();

    Packing packing();

    float price();
}
public interface Packing {
    String pack();
}

實現食物包裝介面的實體類略。

建立實現Item介面的抽象類,該類提供預設的功能,包括漢堡和飲料。

public abstract class Burger implements Item {

    @Override
    public Packing packing() {
        return new Wrapper();
    }

    @Override
    public abstract float price();
}
public abstract class ColdDrink implements Item {

    @Override
    public Packing packing() {
        return new Bottle();
    }

    @Override
    public abstract float price();
}

建立擴充套件Burger和ColdDrink的實體類。

public class ChickenBurger extends Burger {

    @Override
    public float price() {
        return 50.0f;
    }

    @Override
    public String name() {
        return "Chicken Burger";
    }
}
public class VegBurger extends Burger {

    @Override
    public float price() {
        return 25.0f;
    }

    @Override
    public String name() {
        return "Veg Burger";
    }
}
public class Coke extends ColdDrink {

    @Override
    public float price() {
        return 30.0f;
    }

    @Override
    public String name() {
        return "Coke";
    }
}
public class Pepsi extends ColdDrink {

    @Override
    public float price() {
        return 35.0f;
    }

    @Override
    public String name() {
        return "Pepsi";
    }
}

建立一個Meal類,包含具體的Item資訊。

public class Meal {

    private List<Item> items = new ArrayList<>();

    public void addItem(Item item) {
        items.add(item);
    }

    public float getCost() {
        float cost = 0.0f;
        for (Item item : items) {
            cost += item.price();
        }
        return cost;
    }

    public void showItems() {
        for (Item item : items) {
            System.out.print("Item : " + item.name());
            System.out.print(", Packing : " + item.packing().pack());
            System.out.println(", Price : " + item.price());
        }
    }
}

定義抽象建造者,包含建立漢堡和飲料的兩個抽象方法和返回具體Meal的抽象方法。

public abstract class AbstractBuilder {

    public abstract void prepareBurger();

    public abstract void prepareDrink();

    public abstract Meal prepareMeal();
}

建立具體建造者,實現抽象建造者。

public class MealBuilder extends AbstractBuilder {

    private Meal meal = new Meal();
    
    @Override
    public void prepareBurger() {
        meal.addItem(new VegBurger());
    }

    @Override
    public void prepareDrink() {
        meal.addItem(new Coke());
    }

    @Override
    public Meal prepareMeal() {
        return meal;
    }
}

建立指揮者類,在指揮者類中定義setBuilder()方法,在該方法內部實現點餐的逐步構建,典型程式碼如下:

public class MealDirector {

    public void setBuilder(AbstractBuilder builder) {
        builder.prepareBurger();
        builder.prepareDrink();
    }
}

客戶端與指揮者類互動。

public class Client {

    public static void main(String[] args) {
        MealDirector director = new MealDirector();
        MealBuilder builder = new MealBuilder();

        director.setBuilder(builder);
        Meal meal = builder.prepareMeal();
        meal.showItems();
        System.out.println("Total Cost: " + meal.getCost());
    }
}

2. 模式優化

省略Director。在有些情況下,為了簡化系統結構,可以將Director和抽象建造者Builder進行合併,在Builder中提供逐步構建複雜產品物件的construct()方法。

public abstract class AbstractBuilder {

    public static Meal noDirectorMeal = new Meal();

    public abstract void noDirectorPrepareBurger();

    public abstract void noDirectorPrepareDrink();

    public static Meal prepareMeal(AbstractBuilder builder) {
        builder.noDirectorPrepareBurger();
        builder.noDirectorPrepareDrink();
        return noDirectorMeal;
    }
}
public class MealBuilder extends AbstractBuilder {
    @Override
    public void noDirectorPrepareBurger() {
        noDirectorMeal.addItem(new VegBurger());
    }

    @Override
    public void noDirectorPrepareDrink() {
        noDirectorMeal.addItem(new Coke());
    }
}

3. 模式總結

建造者模式的核心在於如何一步步構建一個包含多個組成部件的完整物件,使用相同的構建過程構建不同的產品。

  1. 主要優點
    ① 在建造者模式中,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
    ② 每一個具體建造者都相對獨立,可以很方便地替換具體建造者或增加新的具體建造者。由於指揮者類針對抽象建造者程式設計,增加新的具體建造者無須修改原有類庫的程式碼,系統擴充套件方便,符合“開閉原則”。
  2. 主要缺點
    ① 建造者模式所建立的產品一般具有較多的共同點,如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用範圍受到一定的限制。
    ② 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。
  3. 適用場景
    ① 需要生成的產品物件有複雜的內部結構。
    ② 需要生成的產品物件的屬性相互依賴,需要指定其生成順序。
    ③ 隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品。

4. 思考

如果沒有指揮者類Director,客戶端將如何構建複雜產品?

省略Director的方式。