我們在 DAY6 有用麥當勞的例子來介紹 Facade模式,今天我們再用麥當勞來舉例。假設今天到了麥當勞櫃台,我們不點套餐了,就是單點(雖然比較貴XD),而店員會依照我們要的單點內容來提供餐點,如果把「提供餐點」想成程式碼,或許我們可以建立一個Meal
類別,在這個類別的建構子給他放入漢堡跟可樂,那我們最後就能拿到一個完整的餐點。
但如果不只漢堡跟可樂,我還點了蘋果派呢或者是冰炫風呢?這樣是不是我們就要根據所有的可能組合,為Meal
類別設定非常多可能的建構子。這個時候我們就需要「Builder模式」出馬了!
它可以將複雜對象的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的對象。
(圖片來源:https://www.patrickschadler.com/content/images/2020/10/Builder-Pattern-UML.png)
public class Meal
{
// 需要為每種組合做各別的建構子
public Meal(Hamburger hamburger)
{
}
public Meal(Hamburger hamburger, Drink drink)
{
}
}
IFoodBuilder
是介面,我們會將所有可能的麥當勞餐點,透過一項一個設置方法的方式,來將需要的餐點帶入,也就是說,當我們需要漢堡時,才呼叫「設置漢堡」的方法,將漢堡帶給Meal
類別,這樣就能減少為不同可能性做建構子的事情發生。WaiterDirector
會去實作IFoodBuilder
,在建構時會先建立一個空的Meal
實例,當有呼叫「設置方法」的時候,才會將各別的餐點帶給Meal
實例。WaiterDirectorExtension
是擴充方法,以 C# 的形式去寫,透過靜態類別以及靜態方法,並將參數前面加入this
,就可以實現 meal.set().set()…
這樣的寫法。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("無");
}
}
Builder模式可以解除複雜物件組合時的困難,以上面的例子來看,當我要建立Meal
時,我就不需要再做出很多個建構子,而且這些建構子的參數放入的順序也不用再考慮了,只需要將所需要的餐點 (也就是複雜物件的屬性值)透過指定的設定方法去呼叫,就能夠對Meal
增加所需要的東西。