---
# System prepended metadata

title: 3. Abstract Factory
tags: [DesignPatterns]

---

# 3. Abstract Factory

###### tags: `DesignPatterns`

## 關於 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](https://en.wikipedia.org/wiki/Design_Patterns)

- 提供一個用於創建相關或依賴物件家族(父類別 & 子類別)的接口，且無需指定其具體類別

Abstract Factory(抽象工廠)屬於創建型(Creational Patterns)，當遇到需要**取得兩個以上相互關聯的實體**時，使用 Abstract Factory 來取得實體並將依賴邏輯處理後回傳結果，Abstract Factory 為 [Factory Method](https://waynecheng.coderbridge.io/2021/01/10/Creational-01-FactoryMethod/) 的加強版，同時使用 class 的數量也是加強版。

優點：
- 符合 單一職責原則(Single Responsibility Principle) 
- 符合 開閉原則(Open Closed Principle)

缺點：
- 會增加許多 class 造成程式複雜度增加

---
## 2. UML
![](https://i.imgur.com/OSmqXRd.jpg)
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 轉為程式碼
處理邏輯介面
```C#
/// <summary>
/// 處理邏輯介面 A
/// </summary>
public interface IProductA
{
}

/// <summary>
/// 處理邏輯的介面 B
/// </summary>
public interface IProductB
{
    void Interact(IProductA a);
}
```
處理邏輯實作
```C#
/// <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}");
    }
}
```
抽象工廠的介面
```C#
/// <summary>
/// 抽象工廠介面
/// </summary>
public interface IFactory
{
    IProductA CreateProductA();
    IProductB CreateProductB();
}
```
抽象工廠實作
```C#
/// <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 間依賴邏輯
```C#
/// <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);
    }
}
```

1. 建立工廠`factory`分別傳入`client`
2. 透過`client`處理`product`間邏輯


```C#
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();
}
```
執行結果
```Console
ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2
```

---
## 4. 情境
我們接到了一個付款且要依據付款方式打折的需求
- 需要能支援現有的兩種付款方式(現金、ApplePay)
- 要依據付款方式提供折扣
- 且未來可能會有更多不同的付款方式

建立付款 & 折扣介面
```C#
/// <summary>
/// 付款方式介面
/// </summary>
public interface IPayment
{
    void Pay(int amount);
}

/// <summary>
/// 折扣介面
/// </summary>
public interface IDiscount
{
    void Interact(IPayment payment, int amount);
}
```
實作付款介面
```C#
/// <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} 元");
    }
}
```
實作折扣介面
```C#
/// <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);
    }
}
```
抽象工廠介面
```C#
/// <summary>
/// 抽象工廠介面
/// </summary>
public interface IFactory
{
    IPayment CreatePayment();
    IDiscount CreateDiscount();
}
```
分別實作「現金付款工廠」&「ApplePay 付款工廠」
```C#
/// <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();
    }
}
```
呼叫端，依據傳入的工廠取得付款方式 & 折扣方式
```C#
/// <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);
    }
}
```

1. 建立工廠`cashFactory` & `applePayFactory`分別傳入`Client`
2. 透過`Client`處理付款邏輯 & 折扣邏輯

```C#
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();
}
```
執行結果
```Console
使用 現金 付款 100 元
使用 ApplePay 付款 90 元
```

---
## 完整程式碼
GitHub：[Creational_02_AbstractFactory](https://github.com/darionnnnnn/blog/tree/master/Blog/Creational_02_AbstractFactory)
   
---
## 總結
### Abstract Factory 比 Factory Method 複雜些，滿難一眼就理解其間的差異，在此建議可以試著先將 UML 內 class 之間的關聯釐清後，依據自己的理解將 UML 畫出來，藉此加深對各 Design Pattern 的印象。

---
## 參考資料
1. [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns)
2. [大話設計模式](https://www.tenlong.com.tw/products/9789866761799)
3. [dofactory](https://www.dofactory.com/)
4. [Refactoring.Guru](https://refactoring.guru/)

---

## 新手上路，若有錯誤還請告知，謝謝
