# 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  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/) --- ## 新手上路,若有錯誤還請告知,謝謝
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up