---
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 依賴方法 |
> 
### 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") }
}
```
> 
### 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") }
}
```
> 
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::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 設計模式`