iT邦幫忙

2022 iThome 鐵人賽

DAY 20
1

我們在 DAY6 有用麥當勞的例子來介紹 Facade模式,今天我們再用麥當勞來舉例。假設今天到了麥當勞櫃台,我們不點套餐了,就是單點(雖然比較貴XD),而店員會依照我們要的單點內容來提供餐點,如果把「提供餐點」想成程式碼,或許我們可以建立一個Meal 類別,在這個類別的建構子給他放入漢堡跟可樂,那我們最後就能拿到一個完整的餐點。

但如果不只漢堡跟可樂,我還點了蘋果派呢或者是冰炫風呢?這樣是不是我們就要根據所有的可能組合,為Meal 類別設定非常多可能的建構子。這個時候我們就需要「Builder模式」出馬了!

Builder - 定義

它可以將複雜對象的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的對象。

https://ithelp.ithome.com.tw/upload/images/20220927/20136443aVPBgVODCn.png

(圖片來源:https://www.patrickschadler.com/content/images/2020/10/Builder-Pattern-UML.png)

範例 UML

https://ithelp.ithome.com.tw/upload/images/20220927/20136443gppcO2f1iQ.png

如果不使用Builder模式

public class Meal
{
		// 需要為每種組合做各別的建構子
    public Meal(Hamburger hamburger)
    {
    }

    public Meal(Hamburger hamburger, Drink drink)
    {
    }
}

Code要點

  • IFoodBuilder 是介面,我們會將所有可能的麥當勞餐點,透過一項一個設置方法的方式,來將需要的餐點帶入,也就是說,當我們需要漢堡時,才呼叫「設置漢堡」的方法,將漢堡帶給Meal 類別,這樣就能減少為不同可能性做建構子的事情發生。
  • WaiterDirector會去實作IFoodBuilder ,在建構時會先建立一個空的Meal 實例,當有呼叫「設置方法」的時候,才會將各別的餐點帶給Meal 實例。
  • WaiterDirectorExtension是擴充方法,以 C# 的形式去寫,透過靜態類別以及靜態方法,並將參數前面加入this,就可以實現 meal.set().set()…這樣的寫法。

不囉嗦上Code!

using System;

namespace DAY20_Builder
{
    public class Program
    {
        static void Main(string[] args)
        {
            // Builder模式
            Console.WriteLine("Normal Methods");

            IFoodBuilder builder1 = new WaiterDirector();
            builder1.SetHamburger(new Hamburger("雙層牛肉吉士堡"));
            builder1.SetDrink(new Drink("可樂"));
            Meal meal1 =  builder1.Create();
            Console.WriteLine($"餐點製作完畢!漢堡:{meal1.Hamburger.Name},飲料:{meal1.Drink.Name}");

            IFoodBuilder builder2 = new WaiterDirector();
            builder2.SetHamburger(new Hamburger("勁辣雞腿堡"));
            builder2.Create();
            Meal meal2 = builder2.Create();
            Console.WriteLine($"餐點製作完畢!漢堡:{meal2.Hamburger.Name},飲料:{meal2.Drink.Name}");

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

            // 使用擴充方法
            Console.WriteLine("Extension Methods");

            Meal meal3 = new Meal();
            meal3.SetHamburger(new Hamburger("雙層牛肉吉士堡")).SetDrink(new Drink("可樂")).Create();
            Console.WriteLine($"餐點製作完畢!漢堡:{meal3.Hamburger.Name},飲料:{meal3.Drink.Name}");

            Meal meal4 = new Meal();
            meal4.SetHamburger(new Hamburger("勁辣雞腿堡")).Create();
            Console.WriteLine($"餐點製作完畢!漢堡:{meal4.Hamburger.Name},飲料:{meal4.Drink.Name}");

        }
    }

    public interface IFoodBuilder
    {
        public void SetHamburger(Hamburger hamburger);
        public void SetDrink(Drink drink);
        public Meal Create();
    }

    // Builder模式
    public class WaiterDirector : IFoodBuilder
    {
        private Meal _meal { get; set; }

        public WaiterDirector()
        {
            _meal = new Meal();
        }
        public Meal Create()
        {
            return _meal;
        }

        public void SetDrink(Drink drink)
        {
            _meal.Drink = drink;
        }

        public void SetHamburger(Hamburger hamburger)
        {
            _meal.Hamburger = hamburger;
        }
    }

    // 使用擴充方法,建立靜態類別以及靜態方法
    public static class WaiterDirectorExtension
    {
        // 參數類別前面要帶 this,才能變成擴充方法
        public static Meal Create(this Meal meal)
        {
            return meal;
        }

        public static Meal SetDrink(this Meal meal , Drink drink)
        {
            meal.Drink = drink;
            return meal;
        }

        public static Meal SetHamburger(this Meal meal, Hamburger hamburger)
        {
            meal.Hamburger = hamburger;
            return meal;
        }
    }

    public class Drink
    {
        public string Name { get; set; }
        public Drink(string name)
        {
            Name = name;
        }
    }

    public class Hamburger
    {
        public string Name { get; set; }
        public Hamburger(string name)
        {
            Name = name;
        }
    }

    public class Meal
    {
        public Hamburger Hamburger { get; set; } = new Hamburger("無");
        public Drink Drink { get; set; } = new Drink("無");
    }
}
  • 結果

https://ithelp.ithome.com.tw/upload/images/20220927/20136443Kq1Qlz9xDV.png

簡單的小結

Builder模式可以解除複雜物件組合時的困難,以上面的例子來看,當我要建立Meal 時,我就不需要再做出很多個建構子,而且這些建構子的參數放入的順序也不用再考慮了,只需要將所需要的餐點 (也就是複雜物件的屬性值)透過指定的設定方法去呼叫,就能夠對Meal 增加所需要的東西。


上一篇
【DAY19】Object Pool模式 - 管理你的昂貴物件池
下一篇
【DAY21】Prototype模式 - 無限複製的秘訣!
系列文
勇闖秘境!探索物件導向背後的設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言