2013年5月20日 星期一

設計模式:建造者模式(生成器模式) (Builder Pattern)

建造者模式(生成器模式) (Builder Pattern),以下程式碼以 C# 為例

說明:
將某種類產品,生產步驟整理出來。
所有要生產這類產品的 class,均要實現這些標準化步驟。
另外,為避免實際生產時,遺漏某步驟,統一由一個 class,執行一系列的生產步驟。


範例:
製做大杯珍珠奶茶、小杯紅茶。


希望達成如下的效果
static void Main(string[] args)
{
    Director aa = new Director();
    Bulider bb = new 大杯珍奶();
    aa.setBulider(bb);
    aa.create();

    Console.WriteLine("----------");

    bb = new 小杯紅茶();
    aa.setBulider(bb);
    aa.create();

    Console.ReadLine();
}
執行結果: 
拿大杯子
裝珍珠、裝奶茶
拿大蓋子加蓋
拿粗吸管
----------
拿小杯子
裝紅茶
拿小蓋子加蓋
拿細吸管



實現重點在於,大杯珍奶、小杯紅茶物件,都能傳給 Director 操作。

其餘程式碼
//標準化的生產步驟
interface Bulider
{
    void 拿杯子();

    void 裝飲料();

    void 加蓋子();

    void 拿吸管();
}

//大杯奶茶生產過程,實作 Bulider 介面
class 大杯珍奶 : Bulider
{
    public void 拿杯子()
    {
        Console.WriteLine("拿大杯子");
    }
    public void 裝飲料()
    {
        Console.WriteLine("裝珍珠、裝奶茶");
    }

    public void 加蓋子()
    {
        Console.WriteLine("拿大蓋子加蓋");
    }

    public void 拿吸管()
    {
        Console.WriteLine("拿粗吸管");
    }
}

//小杯紅茶生產過程,實作 Bulider 介面
class 小杯紅茶 : Bulider
{
    public void 拿杯子()
    {
        Console.WriteLine("拿小杯子");
    }
    public void 裝飲料()
    {
        Console.WriteLine("裝紅茶");
    }

    public void 加蓋子()
    {
        Console.WriteLine("拿小蓋子加蓋");
    }

    public void 拿吸管()
    {
        Console.WriteLine("拿細吸管");
    }
}

//統一由指揮者 class 執行生產步驟
class Director
{
    private Bulider builder;

    public void setBulider(Bulider builder)
    {
        this.builder = builder;
    }

    public void create()
    {
        this.builder.拿杯子();
        this.builder.裝飲料();
        this.builder.加蓋子();
        this.builder.拿吸管();
    }
}

相關連結:設計模式整理列表

2 則留言:

  1. 作者您好,
    寫的相當清楚,淺顯易懂,但有個疑問想請教,
    請問
    Director aa = new Director();
    Bulider bb = new 大杯珍奶();
    aa.setBulider(bb);
    aa.create();

    Console.WriteLine("----------");

    bb = new 小杯紅茶();
    aa.setBulider(bb);
    aa.create();



    Drink a = new 大杯珍奶();
    Drink b = new 小杯紅茶();

    // 其中大杯珍奶與小杯紅茶都是繼承Drink類別

    兩種寫法皆是把複雜的創建過程隱藏,那差別在哪裡呢

    回覆刪除
    回覆
    1. 差在耦合性。
      猜測您繼承的Drink父類別,應該包含了建造者模式中Director中的行為(例如create)。

      若之後需要動到Drink父類別時,
      繼承的寫法,須謹慎考量對底下子類別(大杯珍奶、小杯紅茶)會發生什麼影響,
      其他有用到子類別(大杯珍奶、小杯紅茶)的地方,可能都要審視一遍,否則系統可能就爆了。

      若用Director的方式處理,比較像將獨立的物件組合起來一樣,
      更動Director類別,不用擔心對大杯珍奶、小杯紅茶產生什麼影響,
      也不用擔心其他有用到大杯珍奶、小杯紅茶的地方,有沒有什麼其他副作用,修改起來較放心。

      所以有句話說,多用組合、少用繼承,因繼承耦合性高,
      即使是原開發者,若遇到後續需要更動父類別時,要考量的多了,更動不易了,維護成本就高了。

      當然,目前這個例子,用繼承來處理,也沒什麼不妥,
      程式不大、之後的更動基本上能估計,那用什麼方式處理,基本上都差不多。

      刪除