中介者模式:减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行交互。
实现中介者模式的步骤:
- 定义一组会相互调用,拥有强耦合的组件。
- 指定中介者接口以及中介者与各个组件之间的通信方式。在大多数情况下中介者接口中必须有一个Notify/Notification方法从组件接收通知。
- 创建具体中介者实现,该实现将会存储其管理的所有Component对象的引用
- 组件对象应该保存中介者的引用,如果想在不同上下文下使用不同的中介者实现,那么应该通过中介者接口类型保存对具体中介者的引用。
- 将组件对象调用其他组件对象的方法提炼到中介者中,组件对象调用中介者的通知方法,由中介者再去调用相对应的组件的方法,从而完成组件与组件间的解耦。
机场的控制塔是一个典型的中介者角色, 飞机在起飞和降落前都会向控制塔发出问询,控制塔会给飞机发送指令协调它们的起飞降落时间,避免造成事故。现在假设一个机场只有一条跑道,即同一时刻只能承载一架飞机的起飞和降落,飞机和飞机之间不能直接沟通。控制塔作为一个中介者向各个飞机(组件)同步跑道的可用状态。
组件和中介者的 Interface 接口。
// 中介者--机场指挥塔的接口定义
type mediator interface {
canLanding(airplane airplane) bool
notifyAboutDeparture()
}
// 组件--飞行器的接口定义
type airplane interface {
landing()
takeOff()
permitLanding()
}
定义两个组件,一架波音飞机和一架空客飞机。每个飞机在降落landing
方法里都会去跟作为中介者的指挥塔发出问询,看是否能够降落,如果跑道正在被占用,那么会等待指挥塔调用它自己的permitLanding()
通知可以降落后再降落。而其他占用跑道的飞机在起飞后会通过中介者提供的notifyAboutDeparture()
告知指挥塔自己的离去。
// 组件1--波音飞机
type boeingPlane struct {
mediator
}
func (b *boeingPlane) landing() {
if !b.mediator.canLanding(b) {
fmt.Println("Airplane Boeing: 飞机跑到正在被占用,无法降落!")
return
}
fmt.Println("Airplane Boeing: 已成功降落!")
}
func (b *boeingPlane)takeOff() {
fmt.Println("Airplane Boeing: 正在起飞离开跑道!")
b.mediator.notifyAboutDeparture()
}
func (b *boeingPlane)permitLanding() {
fmt.Println("Airplane Boeing: 收到指挥塔信号,允许降落,正在降落!")
b.landing()
}
// 组件2--空客飞机
type airBusPlane struct {
mediator mediator
}
func (airbus *airBusPlane) landing() {
if !airbus.mediator.canLanding(airbus) {
fmt.Println("Airplane AirBus: 飞机跑到正在被占用,无法降落!")
return
}
fmt.Println("Airplane AirBus: 已成功降落!")
}
func (airbus *airBusPlane) takeOff() {
fmt.Println("Airplane AirBus: 正在起飞离开跑道!")
airbus.mediator.notifyAboutDeparture()
}
func (airbus *airBusPlane)permitLanding() {
fmt.Println("Airplane AirBus: 收到指挥塔信号,允许降落,正在降落!")
airbus.landing()
}
作为中介者的指挥塔,提供两个方法
- canLanding:提供给飞机组件问询是否可以降落的方法,如果不可以会把飞机加入到等待队列中,后续跑道空闲后会进行通知。
- notifyAboutDeparture:提供给占用跑道的飞机通知指挥塔已起飞,指挥塔会向排队降落的飞机中的首位发送降落指令–调用飞机对象的
permitLanding
方法
// 中介者实现--指挥塔
type manageTower struct {
isRunwayFree bool
airportQueue []airplane
}
func (tower *manageTower) canLanding(airplane airplane) bool {
if tower.isRunwayFree {
// 跑道空闲,允许降落,同时把状态变为繁忙
tower.isRunwayFree = false
return true
}
// 跑道繁忙,把飞机加入等待通知的队列
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,
}
}
执行函数
func main() {
tower := newManageTower()
boeing := &boeingPlane{
mediator: tower,
}
airbus := &airBusPlane{
mediator: tower,
}
// 波音飞机向指挥塔发送降落请求,跑道空闲,允许降落
boeing.landing()
// 空客飞机向指挥塔发送降落请求,跑道被占用,不允许降落,加入等待队列
airbus.landing()
// 播音飞机向指挥塔发送起飞请求,跑道空闲了,通知等待的空客飞机可以降落,空客飞机降落
boeing.takeOff()
}
中介模式与观察者模式在结构上有些相似,观察者模式中的EventDispatcher 和 中介模式中的 Mediator 看起来很想,都是把多个组件之间的关系,维护到自身,实现组件间的间接通信达到解构效果。
- 观察者模式:组件间的沟通是单向的,从被观察(发送事件的实体)到观察者(监听器),一个参与者要么是观察者要么是被观察者,不会同时兼具两种身份。
- 中介模式:参与者之间可以双向沟通,当参与者之间关系复杂维护成本很高的时候可以考虑中介模式。
当组件类越多时,中介者就会越臃肿,变得复杂且难以维护。