關於 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
& IImplementor
interface
/// <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");
}
}
- 建立
abstraction
並於建構子中載入ConcreteImplementor
- 透過
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} 元");
}
}
- 建立
store
&onlineStore
並於建構子中載入現金 & ApplePay 實作 - 透過
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