Mediator 設計概述
我們在程式設計時往往會收到許多有嚴重相依性的類,這些類有嚴重的耦合關係,密不可分;以類的關係圖來說就是一個類關聯、依賴另一個類,形成了複雜關係(像是三、四角關係,相互關聯)
也就是說,我們往往可以在複雜的類設計中發現,這些設計不符合 迪米特原則(最少知識原則);而Mediator 設計就是解決這個問題的方案之一
Mediator 使用場景
● MVC 架構,其中的 C 層級 (Controller) 就是一個仲介者,調和 View
& Model
之間的通訊
● Mediator 模式可以使用在有強耦合類之間,這些耦合關係在類圖中看起來就像是一個蜘蛛網結構
在物件導向的程式中,物件與物件的依賴關係是必然的(如果沒有依賴關係,那基本上這個類不需要了~)
拓展:網路、藍芽拓撲圖
一般來說網路、藍芽連線拓樸有三種類型,1. 總線型、2. 環形、3. 星形;而 仲介者模式是和處理的則是「星形」拓樸
## 總線型 o--o--o--o--o ## 環形 o / \ o o \ / o ## 星形(Mediator 可以作用於中間的點) o | o--o--o | o
Mediator 定義 & UML
● Mediator 定義:用一個仲介對象來封裝 (encapsulate
) 不同物件之間的溝通,使其變成鬆耦合關係,並且可以獨立改變他們的交互
● Mediator UML 角色關係
角色 | 說明 |
---|---|
Mediator (抽象) | 仲介者的統一方法,調用該方法就可以執行對應的功能 |
ConcreteMeditor | 具體中介,透過調用個個實做 Colleague 來達成目標功能 |
Colleague | 具體的功能實作,主要可以把該類的函數分為兩種,1. Self-Method 改變自身狀態、2. Dep-Method 依賴方法 |
Mediator 實現
接下來我們來實現 Mediator 標準實現、Mediator 抽象化
Mediator 標準實現
A. Colleague
類:各自有自己的功能,可以分開維護,如果有要使用到別的相依類,則透過 Mediator
(以下寫 3 個 Colleague 類)
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
}
}
B. Mediator
類:該類有幾個責任,1. 對外給提供給使用者方法、2. 持有每個 Colleague 實作類
這裡的範例是依賴於各個
Colleague
實作類(不符合依賴倒置),你也可以依照設計重新規劃為抽象類
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()
}
C. ConcreteMeditor
類:真正的實作仲介類,內部使用各個類別的功能來達到使用者所需的功能
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")
}
}
這裡做了個不好的示範:因為可能會造成遞歸的狀況
由於這裡仲介者也對其他的實做類發起行為,操作不當的話很有可能造成遞歸的情況!
這可以 使用測試、規則、紀錄… 等等方式避免,最好的方式是 不要讓仲介去執行內部實做類的方法override fun execute(feat: Feat, vararg params: Any): Any { when (feat) { ... 省略部份 Feat.STOP_PLAYING_READING -> { // 這裡由仲介來呼叫,可能造成遞迴 learn.stopReading() play.stopPlay() } } return Unit }
● 以下 User 使用 Mediator 來達到需要的功能
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 繼承於抽象,如下
A. 抽象 Colleague
類:可以在這個類中宣告公用的方法
當然你也可以把所有 Colleague 類的方法都抽象化,但符合依賴倒置,但是違反了 單一職責
因為抽象類會承攬所有業務方法,但有子類應該只實現與自身相關的方法才對
abstract class AbstractColleague(protected val mediator: Mediator) {
// 宣告共用方法
abstract fun showInfo()
}
B. Colleague
類:實作 showInfo 共用方法
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)")
}
}
C. Mediator
類:實作商業所需邏輯
abstract class Mediator {
... 省略部分
fun showInfo() {
salary.showInfo()
learn.showInfo()
play.showInfo()
}
}
● User 使用幾本上沒有改變
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") }
}
更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
創建模式 Creation Patterns
● 創建模式 PK
● 創建模式 - Creation Patterns
:
創建模式用於「物件的創建」,它關注於如何更靈活、更有效地創建物件。這些模式可以隱藏創建物件的細節,並提供創建物件的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
● Singleton 單例模式 | 解說實現 | Android Framework Context Service
● Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer
● Factory 工廠方法模式 | 解說實現 | Java 集合設計
● Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗
● Clone 原型模式 | 解說實現 | Android Framework Intent
行為模式 Behavioral Patterns
● 行為模式 PK
● 行為模式 - Behavioral Patterns
:
行為模式關注物件之間的「通信」和「職責分配」。它們描述了一系列物件如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
● Stragety 策略模式 | 解說實現 | Android Framework 動畫
● Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService
● Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞
● Specification 規格模式 | 解說實現 | Query 語句實做
● Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計
● Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存
● Visitor 設計模式 | 實現與解說 | 物件導向設計
● Template 設計模式 | 實現與解說 | 物件導向設計
● Mediator 模式設計 | 實現與解說 | 物件導向設計
● Composite 組合模式 | 實現與解說 | 物件導向設計
● Observer 觀察者模式 | JDK Observer | Android Framework Listview
結構模式 Structural Patterns
● 結構模式 PK
● 結構模式 - Structural Patterns
:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
● Decorate 裝飾模式 | 解說實現 | 物件導向設計
● Iterator 迭代設計 | 解說實現 | 物件導向設計