大家好,这里是每周都在陪你一起进步的网管~!今天继续学习设计模式,也是我们要学习的最后一个设计模式—中介者模式,对这个模式有一点了解后会觉得它跟我们已经学过的观察者模式挺像,但是两者还是有些区别的,使用场景也不一样,具体我们放在最后再讲,先来一起学习中介者模式。
中介者模式是一种行为设计模式, 能让程序减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行交互。
中介者模式使修改、扩展和重用单个组件变得容易,因为它们不再依赖于所有其他类。下面我们举一个简单的例子来说明怎么在程序里使用中介者模式减少各个组件类之间的耦合。
在现实生活中,机场的控制塔是一个典型的中介者角色, 飞机在起飞和降落前都会向控制塔发出问询,控制塔会给飞机发送指令协调它们的起飞降落时间,避免造成事故。
现在假设一个机场只有一条跑道,即同一时刻只能承载一架飞机的起飞和降落,飞机和飞机之间不能直接沟通,这样就乱套了,必须由控制塔作为一个中介者向各个飞机(组件)同步跑道的可用状态。
下面我们先来定义飞机和指挥塔--即组件和中介者的 Interface 接口。
"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
// 中介者--机场指挥塔的接口定义
type mediator interface {
canLanding(airplane airplane) bool
notifyAboutDeparture()
}
// 组件--飞行器的接口定义
type airplane interface {
landing()
takeOff()
permitLanding()
}
接下来我们来实现具体的组件,这里提供两个组件作为演示,一架波音飞机和一架空客飞机。
每个飞机在降落landing方法里都会去跟作为中介者的指挥塔发出问询,看是否能够降落,如果跑道正在被占用,那么会等待指挥塔调用它自己的permitLanding()通知可以降落后再降落。而其他占用跑道的飞机在起飞后会通过中介者提供的notifyAboutDeparture() 告知指挥塔自己的离去。
具体的代码如下:
"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
// 组件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方法
"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
// 中介者实现--指挥塔
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,
}
}
这样我们就可以通过指挥塔,协调多个飞机使用飞机场跑道进行有序的起飞和降落了。
"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
func main() {
tower := newManageTower()
boeing := &boeingPlane{
mediator: tower,
}
airbus := &airBusPlane{
mediator: tower,
}
boeing.landing()
airbus.landing()
boeing.takeOff()
}
执行程序后,会有类似下面的输出:
本文的完整源码,已经同步收录到我整理的电子教程里啦,可向我的公众号「网管叨bi叨」发送关键字【设计模式】领取。
看完例子对中介者模式有了一定了解后我们接下来再详细说说它的构成以及用代码实现中介者模式的步骤。
中介者模式的构成
中介者模式的结构构成可以用下面的UML类图来表示
图中的各个类的构成跟我们上面代码实例中列举的十分类似,Component 实现类里需要持有指向中介者的引用,中介者里也保有对各个组件对象的引用,只不过示例里是把组件保存在一个列表里,UML 中是把各个组件单独保存在了中介者的属性里。
下面我们再把用代码实现中介者模式的步骤简单叙述一遍:
- 定义一组会相互调用,拥有强耦合的组件。
- 指定中介者接口以及中介者与各个组件之间的通信方式。在大多数情况下中介者接口中必须有一个Notify/Notification方法从组件接收通知。
- 创建具体中介者实现,该实现将会存储其管理的所有Component对象的引用
- 组件对象应该保存中介者的引用,如果想在不同上下文下使用不同的中介者实现,那么应该通过中介者接口类型保存对具体中介者的引用。
- 将组件对象调用其他组件对象的方法提炼到中介者中,组件对象调用中介者的通知方法,由中介者再去调用相对应的组件的方法,从而完成组件与组件间的解耦。
中介模式与观察者模式区别
中介模式与观察者模式在结构上有些相似,观察者模式中的EventDispatcher 和 中介模式中的 Mediator 看起来很想,都是把多个组件之间的关系,维护到自身,实现组件间的间接通信达到解构效果,不过这两个设计模式在使用场景或者叫要解决的问题上,还是有些差别
- 观察者模式
组件间的沟通是单向的,从被观察(发送事件的实体)到观察者(监听器),一个参与者要么是观察者要么是被观察者,不会同时兼具两种身份。
- 中介模式
参与者之间可以双向沟通,当参与者之间关系复杂维护成本很高的时候可以考虑中介模式。
总结
中介者模式(Mediator Pattern)又叫作调解者模式或调停者模式。 用一个中介对象封装一系列对象交互, 中介者使各对象不需要显式地相互作用, 从而使其耦合松散, 而且可以独立地改变它们之间的交互, 属于行为型设计模式。
中介者模式主要适用于以下应用场景。
- 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
- 交互的公共行为,如果需要改变行为,则可以增加新的中介者类。
中介者模式的优点
- 减少类间依赖,将多对多依赖转化成一对多,降低了类间耦合。
- 类间各司其职,符合迪米特法则。
中介者模式的缺点
- 中介者模式将原本多个对象直接的相互依赖变成了中介者和多个组件类的依赖关系。
- 当组件类越多时,中介者就会越臃肿,变得复杂且难以维护。
最后
今天这篇完结后,用Go学设计模式就正式更新完了,算是一个小小的成就,大家可以在专辑链接里查看系列里的其他文章,后面会写篇总结把设计模式的学习心法给大家说一说,其实就是多看,多练,除此之外也有点小技巧,咱们放到后面给系列收尾时再说。