---
# System prepended metadata

title: 8. Bridge
tags: [DesignPatterns]

---

# 8. Bridge

###### tags: `DesignPatterns`

## 關於 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](https://en.wikipedia.org/wiki/Design_Patterns)

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

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

優點：
- 符合 單一職責原則(Single Responsibility Principle)
- 符合 開閉原則(Open Closed Principle)

缺點：
- 過多的 Bridge 會造成整體程式複雜度提升

---
## 2. UML
![](https://i.imgur.com/oQXidr2.jpg)

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` & `IImplementor`interface
```C#
/// <summary>
/// 包含`Implementor`的抽象介面，在此使用 interface
/// </summary>
public interface IAbstraction
{
    IImplementor Implementor { get; }

    void Operation();
}

/// <summary>
/// 操作的介面
/// </summary>
public interface IImplementor
{
    void Operation();
}
```
實作`IAbstraction`並在建構子取得`IImplementor`實作
```C#
/// <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
```C#
/// <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()`

```C#
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();
}
```
執行結果
```Console
ConcreteImplementorA Operation
ConcreteImplementorB Operation
```

---
## 4. 情境
我們接到了一個線上商城與門市整合付款方式的需求
- 目前有現金、ApplePay 兩種，且未來可能會有更多不同的付款方式
- 除了現有的線上商城與門市，未來可能會有其他銷售通路

定義 門市 & 付款介面
```C#
/// <summary>
/// 門市介面
/// </summary>
public interface IStore
{
    IPayment Payment { get; }

    void Pay(int amount);
}

/// <summary>
/// 付款介面
/// </summary>
public interface IPayment
{
    void Pay(int amount);
}
```
實作實體門市 & 線上商城
```C#
/// <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 付款
```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} 元");
    }
}
```

1. 建立`store` & `onlineStore`並於建構子中載入現金 & ApplePay 實作
2. 透過`Pay()`依據付款方式結帳

```C#
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();
}
```
執行結果
```Console
在實體門市
使用 現金 付款 100 元
在線上商城
使用 ApplePay 付款 200 元
```

---
## 完整程式碼
GitHub：[Structural_02_Bridge](https://github.com/darionnnnnn/blog/tree/master/Blog/Structural_02_Bridge)
   
---
## 總結
### Bridge 與前面介紹的 Adapter 還有之後會介紹的幾種 Pattern 乍看會覺得很相似，但各自用於不同的情境，建議搭配文章中的情境來理解幾個 Pattern 的差異，實際在使用時也可對照文章中的情境來選擇適合的 Pattern，使用起來會更加順手。

---
## 參考資料
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/)

---

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