# 18. Mediator ###### tags: `DesignPatterns` ## 關於 Mediator 本篇將討論以下幾個問題 > ### 1. 關於 Mediator > ### 2. UML > ### 3. 將 UML 轉為程式碼 > ### 4. 情境 --- ## 測試環境: >OS:Windows 10 >IDE:Visual Studio 2019 --- ## 1. 關於 Mediator > Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. > > by [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) - 定義一個一組物件間互動邏輯的封裝(Mediator) - 透過防止物件之間直接互相引用來減低耦合,並且可以獨立修改其互動邏輯 Mediator(中介者)屬於行為型(Behavioral Patterns),當遇到**物件間相互存在複雜引用**時,可藉由 Mediator 來將物件間互動邏輯抽離,減低物件之間耦合,之後若需要修改互動邏輯時則不只需要修改 Mediator 而不需要動到物件本身。 優點: - 符合 單一職責原則(Single Responsibility Principle) - 符合 開閉原則(Open Closed Principle) - 快速解除物件間耦合 缺點: - 過度使用可能會造成 Mediator 演變成 [God Class](https://zh.wikipedia.org/wiki/%E4%B8%8A%E5%B8%9D%E5%AF%B9%E8%B1%A1) --- ## 2. UML  Class 間關聯: - Colleague 關聯 Mediator - ConcreteMediator 繼承 Mediator - ConcreteColleague 繼承 Colleague - ConcreteMediator 關聯 ConcreteColleague Class: - Mediator:中介者的抽象方法或介面 - ConcreteMediator:中介者實作 - Colleague:包含 Mediator 的抽象方法或介面 - ConcreteColleague:Colleague 實作,可經由中介者與其他 ConcreteColleague 互動 --- ## 3. 將 UML 轉為程式碼 中介者的介面 ```C# /// <summary> /// 中介者的介面 /// </summary> public interface IMediator { void Send(string message, IColleague colleague); } ``` 中介者實作 ```C# /// <summary> /// 中介者實作 /// </summary> public class ConcreteMediator : IMediator { public IColleague Colleague1 { get; set; } public IColleague Colleague2 { get; set; } public void Send(string message, IColleague colleague) { if (colleague == Colleague1) { Colleague2.Notify(message); } else { Colleague1.Notify(message); } } } ``` 包含 Mediator 的介面 ```C# /// <summary> /// 包含 Mediator 的介面 /// </summary> public interface IColleague { void Send(string message); void Notify(string message); } ``` Colleague 實作,可經由中介者與其他 ConcreteColleague 互動 ```C# /// <summary> /// Colleague 實作,可經由中介者與其他 ConcreteColleague 互動 /// </summary> public class ConcreteColleague1 : IColleague { private IMediator _mediator { get; } public ConcreteColleague1(IMediator mediator) { _mediator = mediator; } public void Send(string message) { _mediator.Send(message, this); } public void Notify(string message) { Console.WriteLine($"Colleague1 gets message: {message}"); } } /// <summary> /// Colleague 實作,可經由中介者與其他 ConcreteColleague 互動 /// </summary> public class ConcreteColleague2 : IColleague { private IMediator _mediator { get; } public ConcreteColleague2(IMediator mediator) { _mediator = mediator; } public void Send(string message) { _mediator.Send(message, this); } public void Notify(string message) { Console.WriteLine($"Colleague2 gets message: {message}"); } } ``` 1. 建立`mediator` 2. 將`mediator`傳入`colleague1` & `colleague2` 3. 再將`colleague1` & `colleague2`指定給`mediator` 4. `colleague1` & `colleague2`經由`mediator`互動 ```C# static void Main(string[] args) { #region Default Default.ConcreteMediator mediator = new Default.ConcreteMediator(); Default.ConcreteColleague1 colleague1 = new Default.ConcreteColleague1(mediator); Default.ConcreteColleague2 colleague2 = new Default.ConcreteColleague2(mediator); mediator.Colleague1 = colleague1; mediator.Colleague2 = colleague2; colleague1.Send("How are you?"); colleague2.Send("Fine, thanks."); Console.ReadLine(); #endregion } ``` 執行結果 ```Console Colleague2 gets message: How are you? Colleague1 gets message: Fine, thanks. ``` --- ## 4. 情境 我們接到了一個門市取得其他分店庫存的需求 - 不同店之間要能夠取得對方庫存資訊以互相支援 - 各分店販售商品不完全相同,故撈取資料參數不同 中介者的介面 ```C# /// <summary> /// 中介者的介面 /// </summary> public interface IMediator { void GetOtherStoreInStore(IStore store); } ``` 中介者實作 ```C# /// <summary> /// 中介者實作 /// </summary> public class Mediator : IMediator { public IStore StoreDaan { get; set; } public IStore StoreXinyi { get; set; } public void GetOtherStoreInStore(IStore store) { if (store == StoreDaan) { StoreXinyi.GetInStore(); } else { StoreDaan.GetInStore(); } } } ``` 包含 Mediator 的門市介面 ```C# /// <summary> /// 包含 Mediator 的門市介面 /// </summary> public interface IStore { void GetInStore(); } ``` 門市間可透過 Mediator 取得其他分店庫存 ```C# /// <summary> /// 大安店實作,透過 Mediator 取得信義店庫存 /// </summary> public class StoreDaan : IStore { private IMediator _mediator { get; } public StoreDaan(IMediator mediator) { _mediator = mediator; } public void GetOtherStoreInStore() { _mediator.GetOtherStoreInStore(this); } public void GetInStore() { Console.WriteLine($"大安店 目前庫存 咖啡 100 杯,蛋糕 5 片"); } } /// <summary> /// 信義店實作,透過 Mediator 取得大安店庫存 /// </summary> public class StoreXinyi : IStore { private IMediator _mediator { get; } public StoreXinyi(IMediator mediator) { _mediator = mediator; } public void GetOtherStoreInStore() { _mediator.GetOtherStoreInStore(this); } public void GetInStore() { Console.WriteLine($"信義店 目前庫存 咖啡 10 杯,蛋糕 120 片"); } } ``` 1. 建立`mediator` 2. 將`mediator`傳入`storeDaan` & `storeXinyi` 3. 再將`storeDaan` & `storeXinyi`指定給`mediator` 4. 大安店經由`mediator`取得信義店庫存 ```C# static void Main(string[] args) { Situation.Mediator mediator = new Situation.Mediator(); Situation.StoreDaan storeDaan = new Situation.StoreDaan(mediator); Situation.StoreXinyi storeXinyi = new Situation.StoreXinyi(mediator); mediator.StoreDaan = storeDaan; mediator.StoreXinyi = storeXinyi; storeDaan.GetInStore(); storeDaan.GetOtherStoreInStore(); Console.ReadLine(); } ``` 執行結果 ```Console 大安店 目前庫存 咖啡 100 杯,蛋糕 5 片 信義店 目前庫存 咖啡 10 杯,蛋糕 120 片 ``` --- ## 完整程式碼 GitHub:[Behavioral_05_Mediator](https://github.com/darionnnnnn/blog/tree/master/Blog/Behavioral_05_Mediator) --- ## 總結 ### Mediator 為了處理物件間的互動,所以必須直接耦合所有物件,雖然在新增物件互動邏輯時不需要修改物件本身,但在物件日漸增多、邏輯日漸複雜之後,Mediator 很容易變成龐大且難以維護的怪物,適時的拆分 & 重構才能避免問題惡化。 --- ## 參考資料 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