---
# System prepended metadata

title: 11. Facade
tags: [DesignPatterns]

---

# 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/)

---

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