# 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  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/) --- ## 新手上路,若有錯誤還請告知,謝謝
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up