11. Facade


Posted by WayneCheng on 2021-01-18

關於 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

  • 為子系統提供統一的接口
  • Facade 定義了一個更高級別的界面,使子系統更易於使用

Facade(門面)屬於結構型(Structural Patterns),用於整合時,對於系統中包含多個子系統,且子系統各自提供部分服務時,可藉由 Facade 將子系統整合,提供呼叫端完整服務。用於簡化時,對於系統中包含使用方式(e.g. 傳入參數)複雜的服務時,可藉由 Facade 將複雜服務中部分隱藏(簡化),只提供呼叫端部分(簡化過的)服務。

優點:

  • 可簡化複雜系統的使用

缺點:

  • 使用到 Facade 的部分耦合度提高

2. UML

Class 間關聯:

  • Facade 關聯 SubSystem

Class:

  • Facade:整合子系統服務並提供統一的外部接口
  • SubSystem:系統中原有服務

3. 將 UML 轉為程式碼

系統中原有服務 SubSystemA / B / C / D

/// <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 整合子系統並提供外部呼叫接口 MethodA / B

/// <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 / B
static void Main(string[] args)
{
    Default.Facade facade = new Default.Facade();

    facade.MethodA();
    facade.MethodB();

    Console.ReadLine();
}

執行結果

MethodA() ----
SubSystemA Method
SubSystemB Method
SubSystemC Method

MethodB() ----
SubSystemB Method
SubSystemD Method

4. 情境

我們接到了一個取得台北、台中、台南倉庫庫存的需求

  • 取得庫存的 API 已寫好,但參數各不相同
  • 提供一組呼叫介面,未來新增倉庫不用調整呼叫端

各地區取得庫存方法參數各自不同

/// <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 整合各地庫存加總後回傳

/// <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 取得 北台灣庫存 & 全台灣庫存
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();
}

執行結果

 ---- 取得北台灣庫存 ----
台北庫存:100
桃園庫存:80
 ---- 北台灣庫存:180

 ---- 取得全台灣庫存 ----
台北庫存:100
桃園庫存:80
台中庫存:120
台南庫存:200
 ---- 全台灣庫存:500

完整程式碼

GitHub:Structural_05_Facade


總結

Facade 在使用上相當方便,尤其對於在舊有系統上的服務調整,但建議不要為了方便而在 Facade 之上再加上 Facade,會使系統變得複雜且難以維護。另外若是 Facade 越來越肥大時,建議依據功能拆分成多個,以簡化各別 Facade 的職責。


參考資料

  1. Design Patterns
  2. 大話設計模式
  3. dofactory
  4. Refactoring.Guru

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


#designpattern #CSharp







Related Posts

4.1-4.3_使用者輸入輸出與字串物件操作

4.1-4.3_使用者輸入輸出與字串物件操作

wsl 下的 linux crontab 沒有執行問題

wsl 下的 linux crontab 沒有執行問題

Test

Test


Comments