Go設(shè)計(jì)模式--中介者,最后的模式!
大家好,這里是每周都在陪你一起進(jìn)步的網(wǎng)管~!今天繼續(xù)學(xué)習(xí)設(shè)計(jì)模式,也是我們要學(xué)習(xí)的最后一個(gè)設(shè)計(jì)模式—中介者模式,對(duì)這個(gè)模式有一點(diǎn)了解后會(huì)覺(jué)得它跟我們已經(jīng)學(xué)過(guò)的觀察者模式挺像,但是兩者還是有些區(qū)別的,使用場(chǎng)景也不一樣,具體我們放在最后再講,先來(lái)一起學(xué)習(xí)中介者模式。
中介者模式是一種行為設(shè)計(jì)模式, 能讓程序減少對(duì)象之間混亂無(wú)序的依賴關(guān)系。 該模式會(huì)限制對(duì)象之間的直接交互, 迫使它們通過(guò)一個(gè)中介者對(duì)象進(jìn)行交互。
中介者模式使修改、擴(kuò)展和重用單個(gè)組件變得容易,因?yàn)樗鼈儾辉僖蕾囉谒衅渌悺O旅嫖覀兣e一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明怎么在程序里使用中介者模式減少各個(gè)組件類之間的耦合。
在現(xiàn)實(shí)生活中,機(jī)場(chǎng)的控制塔是一個(gè)典型的中介者角色, 飛機(jī)在起飛和降落前都會(huì)向控制塔發(fā)出問(wèn)詢,控制塔會(huì)給飛機(jī)發(fā)送指令協(xié)調(diào)它們的起飛降落時(shí)間,避免造成事故。
現(xiàn)在假設(shè)一個(gè)機(jī)場(chǎng)只有一條跑道,即同一時(shí)刻只能承載一架飛機(jī)的起飛和降落,飛機(jī)和飛機(jī)之間不能直接溝通,這樣就亂套了,必須由控制塔作為一個(gè)中介者向各個(gè)飛機(jī)(組件)同步跑道的可用狀態(tài)。
下面我們先來(lái)定義飛機(jī)和指揮塔--即組件和中介者的 Interface 接口。
"本文使用的完整可運(yùn)行源碼
去公眾號(hào)「網(wǎng)管叨bi叨」發(fā)送【設(shè)計(jì)模式】即可領(lǐng)取"
// 中介者--機(jī)場(chǎng)指揮塔的接口定義
type mediator interface {
canLanding(airplane airplane) bool
notifyAboutDeparture()
}
// 組件--飛行器的接口定義
type airplane interface {
landing()
takeOff()
permitLanding()
}
接下來(lái)我們來(lái)實(shí)現(xiàn)具體的組件,這里提供兩個(gè)組件作為演示,一架波音飛機(jī)和一架空客飛機(jī)。
每個(gè)飛機(jī)在降落landing方法里都會(huì)去跟作為中介者的指揮塔發(fā)出問(wèn)詢,看是否能夠降落,如果跑道正在被占用,那么會(huì)等待指揮塔調(diào)用它自己的permitLanding()通知可以降落后再降落。而其他占用跑道的飛機(jī)在起飛后會(huì)通過(guò)中介者提供的notifyAboutDeparture() 告知指揮塔自己的離去。
具體的代碼如下:
"本文使用的完整可運(yùn)行源碼
去公眾號(hào)「網(wǎng)管叨bi叨」發(fā)送【設(shè)計(jì)模式】即可領(lǐng)取"
// 組件1--波音飛機(jī)
type boeingPlane struct {
mediator
}
func (b *boeingPlane) landing() {
if !b.mediator.canLanding(b) {
fmt.Println("Airplane Boeing: 飛機(jī)跑到正在被占用,無(wú)法降落!")
return
}
fmt.Println("Airplane Boeing: 已成功降落!")
}
func (b *boeingPlane)takeOff() {
fmt.Println("Airplane Boeing: 正在起飛離開(kāi)跑道!")
b.mediator.notifyAboutDeparture()
}
func (b *boeingPlane)permitLanding() {
fmt.Println("Airplane Boeing: 收到指揮塔信號(hào),允許降落,正在降落!")
b.landing()
}
// 組件2--空客飛機(jī)
type airBusPlane struct {
mediator mediator
}
func (airbus *airBusPlane) landing() {
if !airbus.mediator.canLanding(airbus) {
fmt.Println("Airplane AirBus: 飛機(jī)跑到正在被占用,無(wú)法降落!")
return
}
fmt.Println("Airplane AirBus: 已成功降落!")
}
func (airbus *airBusPlane) takeOff() {
fmt.Println("Airplane AirBus: 正在起飛離開(kāi)跑道!")
airbus.mediator.notifyAboutDeparture()
}
func (airbus *airBusPlane)permitLanding() {
fmt.Println("Airplane AirBus: 收到指揮塔信號(hào),允許降落,正在降落!")
airbus.landing()
}
作為中介者的指揮塔,提供兩個(gè)方法
- canLanding:提供給飛機(jī)組件問(wèn)詢是否可以降落的方法,如果不可以會(huì)把飛機(jī)加入到等待隊(duì)列中,后續(xù)跑道空閑后會(huì)進(jìn)行通知。
- notifyAboutDeparture:提供給占用跑道的飛機(jī)通知指揮塔已起飛,指揮塔會(huì)向排隊(duì)降落的飛機(jī)中的首位發(fā)送降落指令--調(diào)用飛機(jī)對(duì)象的permitLanding方法
"本文使用的完整可運(yùn)行源碼
去公眾號(hào)「網(wǎng)管叨bi叨」發(fā)送【設(shè)計(jì)模式】即可領(lǐng)取"
// 中介者實(shí)現(xiàn)--指揮塔
type manageTower struct {
isRunwayFree bool
airportQueue []airplane
}
func (tower *manageTower) canLanding(airplane airplane) bool {
if tower.isRunwayFree {
// 跑道空閑,允許降落,同時(shí)把狀態(tài)變?yōu)榉泵? tower.isRunwayFree = false
return true
}
// 跑道繁忙,把飛機(jī)加入等待通知的隊(duì)列
tower.airportQueue = append(tower.airportQueue, airplane)
return false
}
func (tower *manageTower) notifyAboutDeparture() {
if !tower.isRunwayFree {
tower.isRunwayFree = true
}
if len(tower.airportQueue) > 0 {
firstPlaneInWaitingQueue := tower.airportQueue[0]
tower.airportQueue = tower.airportQueue[1:]
firstPlaneInWaitingQueue.permitLanding()
}
}
func newManageTower() *manageTower {
return &manageTower{
isRunwayFree: true,
}
}
這樣我們就可以通過(guò)指揮塔,協(xié)調(diào)多個(gè)飛機(jī)使用飛機(jī)場(chǎng)跑道進(jìn)行有序的起飛和降落了。
"本文使用的完整可運(yùn)行源碼
去公眾號(hào)「網(wǎng)管叨bi叨」發(fā)送【設(shè)計(jì)模式】即可領(lǐng)取"
func main() {
tower := newManageTower()
boeing := &boeingPlane{
mediator: tower,
}
airbus := &airBusPlane{
mediator: tower,
}
boeing.landing()
airbus.landing()
boeing.takeOff()
}
執(zhí)行程序后,會(huì)有類似下面的輸出:
本文的完整源碼,已經(jīng)同步收錄到我整理的電子教程里啦,可向我的公眾號(hào)「網(wǎng)管叨bi叨」發(fā)送關(guān)鍵字【設(shè)計(jì)模式】領(lǐng)取。
看完例子對(duì)中介者模式有了一定了解后我們接下來(lái)再詳細(xì)說(shuō)說(shuō)它的構(gòu)成以及用代碼實(shí)現(xiàn)中介者模式的步驟。
中介者模式的構(gòu)成
中介者模式的結(jié)構(gòu)構(gòu)成可以用下面的UML類圖來(lái)表示
圖中的各個(gè)類的構(gòu)成跟我們上面代碼實(shí)例中列舉的十分類似,Component 實(shí)現(xiàn)類里需要持有指向中介者的引用,中介者里也保有對(duì)各個(gè)組件對(duì)象的引用,只不過(guò)示例里是把組件保存在一個(gè)列表里,UML 中是把各個(gè)組件單獨(dú)保存在了中介者的屬性里。
下面我們?cè)侔延么a實(shí)現(xiàn)中介者模式的步驟簡(jiǎn)單敘述一遍:
- 定義一組會(huì)相互調(diào)用,擁有強(qiáng)耦合的組件。
- 指定中介者接口以及中介者與各個(gè)組件之間的通信方式。在大多數(shù)情況下中介者接口中必須有一個(gè)Notify/Notification方法從組件接收通知。
- 創(chuàng)建具體中介者實(shí)現(xiàn),該實(shí)現(xiàn)將會(huì)存儲(chǔ)其管理的所有Component對(duì)象的引用
- 組件對(duì)象應(yīng)該保存中介者的引用,如果想在不同上下文下使用不同的中介者實(shí)現(xiàn),那么應(yīng)該通過(guò)中介者接口類型保存對(duì)具體中介者的引用。
- 將組件對(duì)象調(diào)用其他組件對(duì)象的方法提煉到中介者中,組件對(duì)象調(diào)用中介者的通知方法,由中介者再去調(diào)用相對(duì)應(yīng)的組件的方法,從而完成組件與組件間的解耦。
中介模式與觀察者模式區(qū)別
中介模式與觀察者模式在結(jié)構(gòu)上有些相似,觀察者模式中的EventDispatcher 和 中介模式中的 Mediator 看起來(lái)很想,都是把多個(gè)組件之間的關(guān)系,維護(hù)到自身,實(shí)現(xiàn)組件間的間接通信達(dá)到解構(gòu)效果,不過(guò)這兩個(gè)設(shè)計(jì)模式在使用場(chǎng)景或者叫要解決的問(wèn)題上,還是有些差別
- 觀察者模式
組件間的溝通是單向的,從被觀察(發(fā)送事件的實(shí)體)到觀察者(監(jiān)聽(tīng)器),一個(gè)參與者要么是觀察者要么是被觀察者,不會(huì)同時(shí)兼具兩種身份。
- 中介模式
參與者之間可以雙向溝通,當(dāng)參與者之間關(guān)系復(fù)雜維護(hù)成本很高的時(shí)候可以考慮中介模式。
總結(jié)
中介者模式(Mediator Pattern)又叫作調(diào)解者模式或調(diào)停者模式。 用一個(gè)中介對(duì)象封裝一系列對(duì)象交互, 中介者使各對(duì)象不需要顯式地相互作用, 從而使其耦合松散, 而且可以獨(dú)立地改變它們之間的交互, 屬于行為型設(shè)計(jì)模式。
中介者模式主要適用于以下應(yīng)用場(chǎng)景。
- 系統(tǒng)中對(duì)象之間存在復(fù)雜的引用關(guān)系,產(chǎn)生的相互依賴關(guān)系結(jié)構(gòu)混亂且難以理解。
- 交互的公共行為,如果需要改變行為,則可以增加新的中介者類。
中介者模式的優(yōu)點(diǎn)
- 減少類間依賴,將多對(duì)多依賴轉(zhuǎn)化成一對(duì)多,降低了類間耦合。
- 類間各司其職,符合迪米特法則。
中介者模式的缺點(diǎn)
- 中介者模式將原本多個(gè)對(duì)象直接的相互依賴變成了中介者和多個(gè)組件類的依賴關(guān)系。
- 當(dāng)組件類越多時(shí),中介者就會(huì)越臃腫,變得復(fù)雜且難以維護(hù)。
最后
今天這篇完結(jié)后,用Go學(xué)設(shè)計(jì)模式就正式更新完了,算是一個(gè)小小的成就,大家可以在專輯鏈接里查看系列里的其他文章,后面會(huì)寫(xiě)篇總結(jié)把設(shè)計(jì)模式的學(xué)習(xí)心法給大家說(shuō)一說(shuō),其實(shí)就是多看,多練,除此之外也有點(diǎn)小技巧,咱們放到后面給系列收尾時(shí)再說(shuō)。