8. Bridge


Posted by WayneCheng on 2021-01-16

關於 Bridge 本篇將討論以下幾個問題

1. 關於 Bridge

2. UML

3. 將 UML 轉為程式碼

4. 情境


測試環境:

OS:Windows 10
IDE:Visual Studio 2019


1. 關於 Bridge

Decouple an abstraction from its implementation so that the two can vary independently.

by Gang of Four

  • 將抽象與實作分離,使兩者可獨立變化

Bridge(橋接)屬於結構型(Structural Patterns),當遇到兩個維度組合的情況時(e.g. 上衣:長袖短袖,褲子:長褲短褲,就會有長袖 & 長褲長袖 & 短褲短袖 & 長褲短袖 & 短褲、四種組合),用 Bridge 將兩個維度組合起來,則可以不用一開始就將所有組合實作先準備好,而是在呼叫時才決定組合。

優點:

  • 符合 單一職責原則(Single Responsibility Principle)
  • 符合 開閉原則(Open Closed Principle)

缺點:

  • 過多的 Bridge 會造成整體程式複雜度提升

2. UML

Class 間關聯:

  • Client 關聯 Abstraction
  • Abstraction 包含 Implementor 的接口
  • RefinedAbstraction 繼承 Abstraction
  • ConcreteImplementor A / B 繼承 Implementor

Class:

  • Client:呼叫端
  • Abstraction:包含Implementor的抽象類別或介面
  • Implementor:操作的抽象類別或介面
  • RefinedAbstraction:實作Abstraction & Implementor兩者的橋接
  • ConcreteImplementor A / B:操作的實作

3. 將 UML 轉為程式碼

定義IAbstraction & IImplementorinterface

/// <summary>
/// 包含`Implementor`的抽象介面,在此使用 interface
/// </summary>
public interface IAbstraction
{
    IImplementor Implementor { get; }

    void Operation();
}

/// <summary>
/// 操作的介面
/// </summary>
public interface IImplementor
{
    void Operation();
}

實作IAbstraction並在建構子取得IImplementor實作

/// <summary>
/// 實作 IAbstraction 並在建構子取得 IImplementor 實作
/// </summary>
public class RefinedAbstraction : IAbstraction
{
    public IImplementor Implementor { get; }

    public RefinedAbstraction(IImplementor implementor)
    {
        Implementor = implementor;
    }

    public void Operation()
    {
        Implementor.Operation();
    }
}

IImplementor的實作 A / B

/// <summary>
/// IImplementor 的實作 A
/// </summary>
public class ConcreteImplementorA : IImplementor
{
    public void Operation()
    {
        Console.WriteLine("ConcreteImplementorA Operation");
    }
}

/// <summary>
/// IImplementor 的實作 B
/// </summary>
public class ConcreteImplementorB : IImplementor
{
    public void Operation()
    {
        Console.WriteLine("ConcreteImplementorB Operation");
    }
}
  1. 建立abstraction並於建構子中載入ConcreteImplementor
  2. 透過abstraction呼叫ConcreteImplementor中的Operation()
static void Main(string[] args)
{
    Default.IAbstraction abstractionA = new Default.RefinedAbstraction(new Default.ConcreteImplementorA());
    abstractionA.Operation();

    Default.IAbstraction abstractionB = new Default.RefinedAbstraction(new Default.ConcreteImplementorB());
    abstractionB.Operation();

    Console.ReadLine();
}

執行結果

ConcreteImplementorA Operation
ConcreteImplementorB Operation

4. 情境

我們接到了一個線上商城與門市整合付款方式的需求

  • 目前有現金、ApplePay 兩種,且未來可能會有更多不同的付款方式
  • 除了現有的線上商城與門市,未來可能會有其他銷售通路

定義 門市 & 付款介面

/// <summary>
/// 門市介面
/// </summary>
public interface IStore
{
    IPayment Payment { get; }

    void Pay(int amount);
}

/// <summary>
/// 付款介面
/// </summary>
public interface IPayment
{
    void Pay(int amount);
}

實作實體門市 & 線上商城

/// <summary>
/// 實作實體門市
/// </summary>
public class Store : IStore
{
    public IPayment Payment { get; }

    public Store(IPayment payment)
    {
        Payment = payment;
    }

    public void Pay(int amount)
    {
        Console.WriteLine($"在實體門市");
        Payment.Pay(amount);
    }
}

/// <summary>
/// 實作線上商城
/// </summary>
public class OnlineStore : IStore
{
    public IPayment Payment { get; }

    public OnlineStore(IPayment payment)
    {
        Payment = payment;
    }

    public void Pay(int amount)
    {
        Console.WriteLine($"在線上商城");

        Payment.Pay(amount);
    }
}

實作以現金付款 & ApplePay 付款

/// <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} 元");
    }
}
  1. 建立store & onlineStore並於建構子中載入現金 & ApplePay 實作
  2. 透過Pay()依據付款方式結帳
static void Main(string[] args)
{
    Situation.IStore store = new Situation.Store(new Situation.Cash());
    store.Pay(100);

    Situation.IStore onlineStore = new Situation.OnlineStore(new Situation.ApplePay());
    onlineStore.Pay(200);

    Console.ReadLine();
}

執行結果

在實體門市
使用 現金 付款 100 元
在線上商城
使用 ApplePay 付款 200 元

完整程式碼

GitHub:Structural_02_Bridge


總結

Bridge 與前面介紹的 Adapter 還有之後會介紹的幾種 Pattern 乍看會覺得很相似,但各自用於不同的情境,建議搭配文章中的情境來理解幾個 Pattern 的差異,實際在使用時也可對照文章中的情境來選擇適合的 Pattern,使用起來會更加順手。


參考資料

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

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


#designpattern #CSharp







Related Posts

一起來看 Joshua B. Tenenbaum 教授有趣的認知科學研究 - Building Machines that Learn and Think Like People

一起來看 Joshua B. Tenenbaum 教授有趣的認知科學研究 - Building Machines that Learn and Think Like People

[Release Notes] 20200926_v1 - Refined main style to Dark Mode

[Release Notes] 20200926_v1 - Refined main style to Dark Mode

Vite+Vue3佈署上IIS重新整理頁面發生404之解法

Vite+Vue3佈署上IIS重新整理頁面發生404之解法


Comments