# 11. Facade ###### tags: `DesignPatterns` ## 關於 Facade 本篇將討論以下幾個問題 > ### 1. 關於 Facade > ### 2. UML > ### 3. 將 UML 轉為程式碼 > ### 4. 情境 --- ## 測試環境: >OS:Windows 10 >IDE:Visual Studio 2019 --- ## 1. 關於 Facade > Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. > > by [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) - 為子系統提供統一的接口 - Facade 定義了一個更高級別的界面,使子系統更易於使用 Facade(門面)屬於結構型(Structural Patterns),用於**整合**時,對於系統中包含多個子系統,且子系統各自提供部分服務時,可藉由 Facade 將子系統整合,提供呼叫端完整服務。用於**簡化**時,對於系統中包含使用方式(e.g. 傳入參數)複雜的服務時,可藉由 Facade 將複雜服務中部分隱藏(簡化),只提供呼叫端部分(簡化過的)服務。 優點: - 可簡化複雜系統的使用 缺點: - 使用到 Facade 的部分耦合度提高 --- ## 2. UML ![](https://i.imgur.com/pVwNncu.jpg) Class 間關聯: - Facade 關聯 SubSystem Class: - Facade:整合子系統服務並提供統一的外部接口 - SubSystem:系統中原有服務 --- ## 3. 將 UML 轉為程式碼 系統中原有服務 SubSystemA & B & C & D ```C# /// <summary> /// 子系統 A /// </summary> public class SubSystemA { public void Method() { Console.WriteLine("SubSystemA Method"); } } /// <summary> /// 子系統 B /// </summary> public class SubSystemB { public void Method() { Console.WriteLine("SubSystemB Method"); } } /// <summary> /// 子系統 C /// </summary> public class SubSystemC { public void Method() { Console.WriteLine("SubSystemC Method"); } } /// <summary> /// 子系統 D /// </summary> public class SubSystemD { public void Method() { Console.WriteLine("SubSystemD Method"); } } ``` Facade 整合子系統並提供外部呼叫接口 Method A / B ```C# /// <summary> /// Facade 整合子系統並提供外部呼叫接口 /// </summary> public class Facade { private SubSystemA _subSystemA; private SubSystemB _subSystemB; private SubSystemC _subSystemC; private SubSystemD _subSystemD; public Facade() { _subSystemA = new SubSystemA(); _subSystemB = new SubSystemB(); _subSystemC = new SubSystemC(); _subSystemD = new SubSystemD(); } public void MethodA() { Console.WriteLine("\nMethodA() ---- "); _subSystemA.Method(); _subSystemB.Method(); _subSystemC.Method(); } public void MethodB() { Console.WriteLine("\nMethodB() ---- "); _subSystemB.Method(); _subSystemD.Method(); } } ``` 1. 建立`facade` 2. 經由`facade`呼叫 MethodA & MethodB ```C# static void Main(string[] args) { Default.Facade facade = new Default.Facade(); facade.MethodA(); facade.MethodB(); Console.ReadLine(); } ``` 執行結果 ```Console MethodA() ---- SubSystemA Method SubSystemB Method SubSystemC Method MethodB() ---- SubSystemB Method SubSystemD Method ``` --- ## 4. 情境 我們接到了一個取得台北、台中、台南倉庫庫存的需求 - 取得庫存的 API 已寫好,但參數各不相同 - 提供一組呼叫介面,未來新增倉庫不用調整呼叫端 各地區取得庫存方法參數各自不同 ```C# /// <summary> /// 台北庫存 /// </summary> public class TaipeiInStock { public int GetInStock(int parameterA, string parameterB) { Console.WriteLine("台北庫存:100"); return 100; } } /// <summary> /// 桃園庫存 /// </summary> public class TaoyuanInStock { public int GetInStock(int parameterA, string parameterB) { Console.WriteLine("桃園庫存:80"); return 80; } } /// <summary> /// 台中庫存 /// </summary> public class TaichungInStock { public int GetInStock(bool parameterC) { Console.WriteLine("台中庫存:120"); return 120; } } /// <summary> /// 台南庫存 /// </summary> public class TainanInStock { public int GetInStock() { Console.WriteLine("台南庫存:200"); return 200; } } ``` Facade 整合各地庫存加總後回傳 ```C# /// <summary> /// Facade 整合各地庫存加總後回傳 /// </summary> public class Facade { private TaipeiInStock _taipeiInStock; private TaoyuanInStock _taoyuanInStock; private TaichungInStock _taichungInStock; private TainanInStock _tainanInStock; public Facade() { _taipeiInStock = new TaipeiInStock(); _taoyuanInStock = new TaoyuanInStock(); _taichungInStock = new TaichungInStock(); _tainanInStock = new TainanInStock(); } // 取得北台灣庫存 public int GetNorthernTaiwanInStock() { Console.WriteLine("\n ---- 取得北台灣庫存 ---- "); var taipei = _taipeiInStock.GetInStock(default, default); var taoyuan = _taoyuanInStock.GetInStock(default, default); return taipei + taoyuan; } // 取得全台灣庫存 public int GetTaiwanInStock() { Console.WriteLine("\n ---- 取得全台灣庫存 ---- "); var taipei = _taipeiInStock.GetInStock(default, default); var taoyuan = _taoyuanInStock.GetInStock(default, default); var taichung = _taichungInStock.GetInStock(default); var tainan = _tainanInStock.GetInStock(); return taipei + taoyuan + taichung + tainan; } } ``` 1. 建立`facade` 2. 經由`facade`取得 北台灣庫存 & 全台灣庫存 ```C# static void Main(string[] args) { Situation.Facade facade = new Situation.Facade(); var northernTaiwanInStock = facade.GetNorthernTaiwanInStock(); Console.WriteLine($" ---- 北台灣庫存:{northernTaiwanInStock}"); var taiwanInStock = facade.GetTaiwanInStock(); Console.WriteLine($" ---- 全台灣庫存:{taiwanInStock}"); Console.ReadLine(); } ``` 執行結果 ```Console ---- 取得北台灣庫存 ---- 台北庫存:100 桃園庫存:80 ---- 北台灣庫存:180 ---- 取得全台灣庫存 ---- 台北庫存:100 桃園庫存:80 台中庫存:120 台南庫存:200 ---- 全台灣庫存:500 ``` --- ## 完整程式碼 GitHub:[Structural_05_Facade](https://github.com/darionnnnnn/blog/tree/master/Blog/Structural_05_Facade) --- ## 總結 ### Facade 在使用上相當方便,尤其對於在舊有系統上的服務調整,但建議不要為了方便而在 Facade 之上再加上 Facade,會使系統變得複雜且難以維護。另外若是 Facade 越來越肥大時,建議依據功能拆分成多個,以簡化個別 Facade 的職責。 --- ## 參考資料 1. [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) 2. [大話設計模式](https://www.tenlong.com.tw/products/9789866761799) 2. [dofactory](https://www.dofactory.com/) 3. [Refactoring.Guru](https://refactoring.guru/) --- ## 新手上路,若有錯誤還請告知,謝謝