關於 Abstract Factory 本篇將討論以下幾個問題
1. 關於 Abstract Factory
2. UML
3. 將 UML 轉為程式碼
4. 情境
測試環境:
OS:Windows 10
IDE:Visual Studio 2019
1. 關於 Abstract Factory
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
by Gang of Four
- 提供一個用於創建相關或依賴物件家族(父類別 & 子類別)的接口,且無需指定其具體類別
Abstract Factory(抽象工廠)屬於創建型(Creational Patterns),當遇到需要取得兩個以上相互關聯的實體時,使用 Abstract Factory 來取得實體並將依賴邏輯處理後回傳結果,Abstract Factory 為 Factory Method 的加強版,同時使用 class 的數量也是加強版。
優點:
- 符合 單一職責原則(Single Responsibility Principle)
- 符合 開閉原則(Open Closed Principle)
缺點:
- 會增加許多 class 造成程式複雜度增加
2. UML
Class 間關聯:
- Product A1 / A2 繼承 AbstractProductA
- Product B1 / B2 繼承 AbstractProductB
- ConcreteFactory 1 / 2 繼承 AbstractFactory
- Client 關聯 AbstractProduct A / B & AbstractFactory
Class:
- Client:呼叫端,透過 Factory 取得多個 Product 實體,並處理其關聯邏輯
- AbstractProduct:處理邏輯的抽象類別或介面
- Product:處理邏輯實作
- AbstractFactory:抽象工廠的抽象類別或介面
- ConcreteFactory:抽象工廠實作,用來創建處理邏輯實體
3. 將 UML 轉為程式碼
處理邏輯介面
/// <summary>
/// 處理邏輯介面 A
/// </summary>
public interface IProductA
{
}
/// <summary>
/// 處理邏輯的介面 B
/// </summary>
public interface IProductB
{
void Interact(IProductA a);
}
處理邏輯實作
/// <summary>
/// 處理邏輯介面 A 實作 A1
/// </summary>
public class ProductA1 : IProductA
{
}
/// <summary>
/// 處理邏輯介面 B 實作 B1
/// </summary>
class ProductB1 : IProductB
{
public void Interact(IProductA a)
{
Console.WriteLine($"{this.GetType().Name} interacts with {a.GetType().Name}");
}
}
/// <summary>
/// 處理輯介面 A 實作 A2
/// </summary>
public class ProductA2 : IProductA
{
}
/// <summary>
/// 處理邏輯介面 B 實作 B2
/// </summary>
class ProductB2 : IProductB
{
public void Interact(IProductA a)
{
Console.WriteLine($"{this.GetType().Name} interacts with {a.GetType().Name}");
}
}
抽象工廠的介面
/// <summary>
/// 抽象工廠介面
/// </summary>
public interface IFactory
{
IProductA CreateProductA();
IProductB CreateProductB();
}
抽象工廠實作
/// <summary>
/// 抽象工廠實作 1
/// </summary>
public class ConcreteFactory1 : IFactory
{
public IProductA CreateProductA()
{
return new ProductA1();
}
public IProductB CreateProductB()
{
return new ProductB1();
}
}
/// <summary>
/// 抽象工廠實作 2
/// </summary>
public class ConcreteFactory2 : IFactory
{
public IProductA CreateProductA()
{
return new ProductA2();
}
public IProductB CreateProductB()
{
return new ProductB2();
}
}
呼叫端,透過 factory 取得 Product 實作,並處理 Product 間依賴邏輯
/// <summary>
/// 呼叫端,透過 factory 取得 Product 實作,並處理 Product 間依賴邏輯
/// </summary>
public class Client
{
private IProductA _productA;
private IProductB _productB;
public Client(IFactory factory)
{
_productB = factory.CreateProductB();
_productA = factory.CreateProductA();
}
public void Run()
{
_productB.Interact(_productA);
}
}
- 建立工廠
factory
分別傳入client
- 透過
client
處理product
間邏輯
static void Main(string[] args)
{
Default.IFactory factory1 = new Default.ConcreteFactory1();
Default.Client client1 = new Default.Client(factory1);
client1.Run();
Default.IFactory factory2 = new Default.ConcreteFactory2();
Default.Client client2 = new Default.Client(factory2);
client2.Run();
Console.ReadLine();
}
執行結果
ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2
4. 情境
我們接到了一個付款且要依據付款方式打折的需求
- 需要能支援現有的兩種付款方式(現金、ApplePay)
- 要依據付款方式提供折扣
- 且未來可能會有更多不同的付款方式
建立付款 & 折扣介面
/// <summary>
/// 付款方式介面
/// </summary>
public interface IPayment
{
void Pay(int amount);
}
/// <summary>
/// 折扣介面
/// </summary>
public interface IDiscount
{
void Interact(IPayment payment, int amount);
}
實作付款介面
/// <summary>
/// 實作付款介面:現金
/// </summary>
public class Cash : IPayment
{
public void Pay(int amount)
{
Console.WriteLine($"使用 現金 付款 {amount} 元");
}
}
/// <summary>
/// 實作付款介面:ApplePay
/// </summary>
public class ApplePay : IPayment
{
public void Pay(int amount)
{
Console.WriteLine($"使用 ApplePay 付款 {amount} 元");
}
}
實作折扣介面
/// <summary>
/// 實作折扣介面:現金
/// </summary>
class DiscountCash : IDiscount
{
public void Interact(IPayment payment, int amount)
{
// 使用現金無折扣
payment.Pay(amount);
}
}
/// <summary>
/// 實作折扣介面:ApplePay
/// </summary>
class DiscountApplePay : IDiscount
{
public void Interact(IPayment payment, int amount)
{
// 使用 ApplePay 打九折
var newAmount = (int)(amount * 0.9);
payment.Pay(newAmount);
}
}
抽象工廠介面
/// <summary>
/// 抽象工廠介面
/// </summary>
public interface IFactory
{
IPayment CreatePayment();
IDiscount CreateDiscount();
}
分別實作「現金付款工廠」&「ApplePay 付款工廠」
/// <summary>
/// 現金付款工廠
/// </summary>
public class CashFactory : IFactory
{
public IPayment CreatePayment()
{
return new Cash();
}
public IDiscount CreateDiscount()
{
return new DiscountCash();
}
}
/// <summary>
/// ApplePay 付款工廠
/// </summary>
public class ApplePayFactory : IFactory
{
public IPayment CreatePayment()
{
return new ApplePay();
}
public IDiscount CreateDiscount()
{
return new DiscountApplePay();
}
}
呼叫端,依據傳入的工廠取得付款方式 & 折扣方式
/// <summary>
/// 取得付款 & 折扣實作,並可進行付款動作
/// </summary>
public class Client
{
private IPayment _payment;
private IDiscount _discount;
public Client(IFactory factory)
{
_discount = factory.CreateDiscount();
_payment = factory.CreatePayment();
}
public void Pay(int amount)
{
_discount.Interact(_payment, amount);
}
}
- 建立工廠
cashFactory
&applePayFactory
分別傳入Client
- 透過
Client
處理付款邏輯 & 折扣邏輯
static void Main(string[] args)
{
Situation.IFactory cashFactory = new Situation.CashFactory();
Situation.Client cashClient = new Situation.Client(cashFactory);
cashClient.Pay(100);
Situation.IFactory applePayFactory = new Situation.ApplePayFactory();
Situation.Client applePayClient = new Situation.Client(applePayFactory);
applePayClient.Pay(100);
Console.ReadLine();
}
執行結果
使用 現金 付款 100 元
使用 ApplePay 付款 90 元
完整程式碼
GitHub:Creational_02_AbstractFactory