--- title: 'Mediator 仲介模式' disqus: kyleAlien --- Mediator 仲介模式 === 如有引用,請標明出處 :smile_cat: ## Overview of Content :::success * 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/) 本篇文章對應的是 [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/) ::: [TOC] ## Mediator 設計概述 我們在程式設計時往往會收到許多有嚴重相依性的類,這些類有嚴重的耦合關係,密不可分;以類的關係圖來說就是一個類關聯、依賴另一個類,形成了複雜關係 :::warning 也就是說,我們往往可以在複雜的類設計中發現,這些設計不符合 [迪米特原則](https://devtechascendancy.com/object-oriented-design-principles_2/#%E8%BF%AA%E7%B1%B3%E7%89%B9%E5%8E%9F%E5%89%87_%E2%80%93_Low_of_Demeter)(最少知識原則);而Mediator 設計就是解決這個問題的方案之一 ::: ### Mediator 使用場景 * MVC 架構,其中的 C 層級 (Controller) 就是一個仲介者,調和 `View` & `Model` 之間的通訊 * Mediator 模式可以使用在有強耦合類之間,這些耦合關係在類圖中看起來就像是一個蜘蛛網結構 > 在物件導向的程式中,物件與物件的依賴關係是必然的(如果沒有依賴關係,那基本上這個類不需要了~) :::info * **網路、藍芽拓撲圖** 一般來說網路、藍芽連線拓樸有三種類型,^1.^ 總線型、^2.^ 環形、^3.^ 星形;而 **仲介者模式是和處理的則是「星形」拓樸** ```shell= ## 總線型 o--o--o--o--o ## 環形 o / \ o o \ / o ## 星形(Mediator 可以作用於中間的點) o | o--o--o | o ``` ::: ### Mediator 定義 & Mediator UML 設計 * Mediator 定義:用一個仲介對象來封裝 (encapsulate) 不同物件之間的溝通,使其變成鬆耦合關係,並且可以獨立改變他們的交互 * Mediator UML 角色關係 | 角色 | 說明 | | - | - | | Mediator (抽象) | 仲介者的統一方法,調用該方法就可以執行對應的功能 | | ConcreteMeditor | 具體中介,透過調用個個實做 Colleague 來達成目標功能 | | Colleague | 具體的功能實作,主要可以把該類的函數分為兩種,^1.^ Self-Method 改變自身狀態、^2.^ Dep-Method 依賴方法 | > ![](https://i.imgur.com/cYQMO4H.png) ### Mediator 設計 - 優缺點 * 最明顯的優點就在於:可以把三角關係(或更多關係)的類進行切分,讓其轉變為一對一個關係 > 全部改成依賴 `Mediator` 的抽象核心,這是為了讓其滿足 [**依賴倒置**](https://devtechascendancy.com/object-oriented-design-principles_1/#%E4%BE%9D%E8%B3%B4%E5%80%92%E7%BD%AE%E5%8E%9F%E5%89%87_%E2%80%93_Dependence_Inversion) * Mediator 缺點:`Mediator` 抽象類的成員會依照需求手動增加,本身不符合[**開閉原則**](https://devtechascendancy.com/object-oriented-design-principles_2/#%E9%96%8B%E9%96%89%E5%8E%9F%E5%89%87_%E2%80%93_Open_Close_Principle),必須依照需求不斷地去修改其(ConcreteMediator)功能,並且邏輯複雜 > `Mediator` 成員越多邏輯越複雜 ## Mediator 實現 接下來我們來實現 Mediator 標準實現、Mediator 抽象化 ### Mediator 標準 1. **`Colleague` 類**:各自有自己的功能,可以分開維護,如果有要使用到別的相依類,則透過 `Mediator` > 以下寫 3 個 Colleague 類 ```kotlin= class MonthSalary constructor(private val mediator: Mediator) { var salary: Int = 30000 fun useMoney(used: Int) { if (salary <= 0) { return } salary -= used println("After useMoney, have \$$salary") } fun sickDay(days: Int) { println("Before sick, have \$$salary") salary -= days * 10 mediator.execute(Mediator.Feat.STOP_PLAYING_READING) println("After sick, just have \$$salary") } } class Learning constructor(private val mediator: Mediator) { private val bookList : MutableList<String> = mutableListOf() var learningProgress = 0 fun buyBook(name: String) { mediator.run { // 檢查薪水 if (execute(Mediator.Feat.CHECK_SALARY) as Boolean) { // 買書 execute(Mediator.Feat.USE_MONEY, 300).also { // 自身邏輯 bookList.add(name) println("Buy Book") } } else { println("No money to buy") } } } fun readingBook() { learningProgress += 10 println("Reading... $learningProgress") } fun stopReading() { println("Stop reading...") } } class PlaySomething constructor(private val mediator: Mediator) { private var curGame : String? = null fun playOnlineGame(gameName: String) { mediator.run { // 檢查薪水 if ((execute(Mediator.Feat.LEARNING_PROGRESS) as Int) < 0) { println("Learning first.") return } if (execute(Mediator.Feat.CHECK_SALARY) as Boolean) { // 買書 execute(Mediator.Feat.USE_MONEY, 500).also { // 自身邏輯 curGame = gameName println("Playing game ... $gameName") } } else { println("No money to play.") } } } fun stopPlay() { curGame = null } } ``` 2. **`Mediator` 類**:該類有幾個責任,^1.^ 對外給提供給使用者方法、^2.^ 持有每個 Colleague 實作類 > 這裡的範例是依賴於各個 `Colleague` 實作類(不符合依賴倒置),你也可以依照設計重新規劃為抽象類 ```kotlin= abstract class Mediator { enum class Feat { USE_MONEY, CHECK_SALARY, LEARNING_PROGRESS, STOP_PLAYING_READING } protected val salary = lazy { MonthSalary(this) }.value protected val learn = lazy { Learning(this) }.value protected val play = lazy { PlaySomething(this) }.value // 接收各個實做的操控 abstract fun execute(feat: Feat, vararg params: Any) : Any // 業務邏輯 abstract fun sick() abstract fun learning() abstract fun play() } ``` 3. **`ConcreteMeditor` 類**:真正的實作仲介類,內部使用各個類別的功能來達到使用者所需的功能 ```kotlin= class ConcreteMediator : Mediator() { override fun execute(feat: Feat, vararg params: Any): Any { when (feat) { Feat.USE_MONEY -> { salary.useMoney(params[0] as Int) } Feat.CHECK_SALARY -> { return salary.salary > 0 } Feat.LEARNING_PROGRESS -> { return learn.learningProgress } Feat.STOP_PLAYING_READING -> { learn.stopReading() play.stopPlay() } } return Unit } override fun sick() { salary.sickDay(3) } override fun learning() { learn.buyBook("Hello World") learn.readingBook() } override fun play() { play.playOnlineGame("Maple story") } } ``` :::danger * **這裡做了個不好的示範:因為可能會造成遞歸的狀況** 由於這裡仲介者也對其他的實做類發起行為,操作不當的話很有可能造成遞歸的情況! 這可以 **使用測試、規則、紀錄... 等等方式避免,最好的方式是 不要讓仲介去執行內部實做類的方法** ```kotlin= override fun execute(feat: Feat, vararg params: Any): Any { when (feat) { ... 省略部份 Feat.STOP_PLAYING_READING -> { // 這裡由仲介來呼叫,可能造成遞迴 learn.stopReading() play.stopPlay() } } return Unit } ``` ::: * 以下 User 使用 Mediator 來達到需要的功能 ```kotlin= fun main() { val mediator: Mediator = ConcreteMediator() mediator.learning().also { println("\n") } mediator.sick().also { println("\n") } mediator.play().also { println("\n") } } ``` > ![](https://i.imgur.com/jSvXDPc.png) ### Mediator 抽象化 * 從上面我們可以看出各個 Colleague 都會依賴於 Mediator (抽象) 類;這邊我們可以拓展讓 Colleague 繼承於抽象,如下 1. **抽象 `Colleague` 類**:可以在這個類中宣告公用的方法 :::success * 當然你也可以把所有 Colleague 類的方法都抽象化,但符合依賴倒置,但是違反了 **單一職責** > 因為抽象類會承攬所有業務方法,但有子類應該只實現與自身相關的方法才對 ::: ```kotlin= abstract class AbstractColleague(protected val mediator: Mediator) { // 宣告共用方法 abstract fun showInfo() } ``` 2. **`Colleague` 類**:實作 showInfo 共用方法 ```kotlin= class PlaySomething constructor(mediator: Mediator) : AbstractColleague(mediator) { ... 省略部分 override fun showInfo() { println("curGame=($curGame)") } } class Learning constructor(mediator: Mediator) : AbstractColleague(mediator) { ... 省略部分 override fun showInfo() { println("learningProgress=($learningProgress), list=($bookList)") } } class MonthSalary constructor(mediator: Mediator) : AbstractColleague(mediator) { ... 省略部分 override fun showInfo() { println("salary=($salary)") } } ``` 2. **`Mediator` 類**:實作商業所需邏輯 ```kotlin= abstract class Mediator { ... 省略部分 fun showInfo() { salary.showInfo() learn.showInfo() play.showInfo() } } ``` * User 使用幾本上沒有改變 ```kotlin= fun main() { val mediator: Mediator = ConcreteMediator() mediator.learning().also { println("\n") } mediator.sick().also { println("\n") } mediator.play().also { println("\n") } mediator.showInfo().also { println("\n") } } ``` > ![](https://i.imgur.com/fFhgfup.png) ## 更多的物件導向設計 物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)! :::info * [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/) * [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/) * [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/) ::: ### 創建模式 - Creation Patterns * [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/) * **創建模式 - `Creation Patterns`**: 創建模式用於「**物件的創建**」,它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節,並提供創建對象的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結 :::success * [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/) * [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/) * [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/) * [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/) * [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/) * [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/) * [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/) ::: ### 行為模式 - Behavioral Patterns * [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/) * **行為模式 - `Behavioral Patterns`**: 行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結 :::warning * [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/) * [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/) * [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/) * [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/) * [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/) * [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/) * [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/) * [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/) * [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/) * [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/) * [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/) ::: ### 結構模式 - Structural Patterns * [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/) * **結構模式 - `Structural Patterns`**: 結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結 :::danger * [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/) * [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/) * [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/) * [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/) * [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/) * [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/) ::: ## Appendix & FAQ :::info ::: ###### tags: `Java 設計模式`