中介者模式的做用就是解除对象与对象之间的紧耦合关系,全部对象经过中介者来通讯,而不是互相引用,因此当一个对象发生改变时,只须要通知中介者对象便可。
javascript
下面经过一个泡泡糖游戏来介绍中介者模式的应用。
在刚开始设计游戏的时候,只支持两个玩家同时进行对战。首先定义一个玩家类,它有三个方法win、lose、die,由于玩家的数目是2,因此当其中一个玩家死亡游戏并结束了,同时通知他的对手胜利。因而有下面的代码:java
class Player {
constructor (name) {
this.name = name
this.enemy = null // 敌人
}
win () {
console.log(`${this.name} won`)
}
lose () {
console.log(`${this.name} lost`)
}
die () {
this.lose()
this.enemy.win()
}
}
// 建立两个玩家
const player1 = new Player('蘑菇头')
const player2 = new Player('闰土')
// 给玩家互设敌人
player1.enemy = player2
player2.enemy = player1
复制代码
当玩家player1死亡的时候,只须要调用die方法,并结束了这局游戏:数组
player1.die() // 蘑菇头 lost,闰土 won
复制代码
两个玩家的游戏没什么意思,知足不了一堆玩家一块儿玩,因而加了需求,能够组队玩。
给游戏增长队伍
首先定义一个数组players保存全部玩家,每次建立玩家,循环players给每一个玩家添加敌人或者队友,而后给玩家添加队友列表、敌人列表、队伍颜色、玩家状态,因此咱们改写Player类:app
let players = [] //
class Player {
constructor (name, teamColor) {
this.name = name
this.partners = []
this.enemies = []
this.state = 'live'
this.teamColor = teamColor
}
win () {
console.log(`${this.name} won`)
}
lose () {
console.log(`${this.name} lost`)
}
die () {
let all_dead = true
this.state = 'dead' // 修改玩家的状态
for (let i = 0, partner; partner = this.partners[i++];) {
if (partner.state !== 'dead') {
all_dead = false
break
}
}
if (all_dead === true) {
this.lose()
for (let i = 0, partner; partner = this.partners[i++];) {
partner.lose()
}
for (let i = 0, enemy; enemy = this.enemies[i++];) {
enemy.win()
}
}
}
}
复制代码
最后定义工厂函数来建立玩家:函数
const playerFactory = function (name, teamColor) {
const newPlayer = new Player(name, teamColor)
for (let i = 0, player; player = players[i++];) {
if (player.teamColor === newPlayer.teamColor) {
player.partners.push(newPlayer)
newPlayer.partners.push(player)
} else {
player.enemies.push(newPlayer)
newPlayer.enemies.push(player)
}
}
players.push(newPlayer)
return newPlayer
}
复制代码
建立玩家两队玩家:测试
const player1 = playerFactory('A', 'red')
const player2 = playerFactory('B', 'red')
const player3 = playerFactory('C', 'red')
const player4 = playerFactory('D', 'red')
const player5 = playerFactory('E', 'blue')
const player6 = playerFactory('F', 'blue')
const player7 = playerFactory('G', 'blue')
const player8 = playerFactory('H', 'blue')
复制代码
而后让红队玩家所有死亡:ui
player1.die() // A lost
player2.die() // B lost
player3.die() // C lost
player4.die() // D lost, E won、 F won、 G won、 H won
复制代码
上面的代码虽然能完成最后咱们想要的效果,可是每一个玩家之间都是紧密相关的,每一个玩家都会有一个partners和enemyies来保存其它玩家的引用,任何一个玩家死亡或者进行其它操做,都要显示地通知其它玩家。在上面的例子中,只有8个玩家可能尚未对你产生足够多的困扰。可是在实际应用中,每一个游戏都有成千上万的玩家,几十支队伍互相厮杀,这时候玩家状态改变,若是经过循环去通知其它玩家,估计游戏随时会崩溃。
用中介者模式改造游戏
首先仍是定义玩家类,只不过在玩家的类方法中,不须要负责执行具体的逻辑,而是把操做交给中介者,咱们命名中介者为playDirector。看代码:this
class Player {
constructor (name, teamColor) {
this.name = name
this.teamColor = teamColor
this.state = 'live'
}
win () {
console.log(`${this.name} won`)
}
lose () {
console.log(`${this.name} lost`)
}
die () {
this.state = 'dead'
playDirector.receiveMessage('playerDead', this)
}
// 移除玩家
remove () {
playDirector.receiveMessage('removePlayer', this)
}
// 玩家换队
changeTeam () {
playDirector.receiveMessage('changeTeam', this)
}
}
复制代码
改写建立玩家的工厂函数,这个工厂只须要建立玩家,不须要给玩家设置队友和敌人:spa
const playerFactory = function (name, teamColor) {
const newPlayer = new Player(name, teamColor)
playDirector.receiveMessage('addPlayer', newPlayer)
return newPlayer
}
复制代码
经过前面的代码,咱们能够看出,中介者须要暴露一个receiveMessage接口,负责接收player对象发送的消息,而后中介者收到消息后进行处理。下面实现中介者:prototype
const playDirector = (function () {
let players = {}, // 保存全部玩家
operations = {} // 中介者能够执行的操做
operations.addPlayer = function (player) {
const teamColor = player.teamColor
players[teamColor] = players[teamColor] || []
players[teamColor].push(player)
}
operations.removePlayer = function (player) {
let teamColor = player.teamColor,
teamPlayers = players[teamColor] || []
teamPlayers = teamPlayers.filter((item) => {
return item !== player
})
}
operations.changeTeam = function (player, teamColor) {
operations.removePlayer(player)
player.teamColor = teamColor
operations.addPlayer(player)
}
operations.playerDead = function (player) {
let teamColor = player.teamColor,
teamPlayers = players[teamColor] || []
let all_dead = true
this.state = 'dead' // 修改玩家的状态
for (let i = 0, player; player = teamPlayers[i++];) {
if (player.state !== 'dead') {
all_dead = false
break
}
}
if (all_dead === true) {
for (let i = 0, player; player = teamPlayers[i++];) {
player.lose()
}
for (let color in players) {
if (color !== teamColor) {
let teamPlayers = players[color]
for (let i = 0, player; player = teamPlayers[i++];) {
player.win()
}
}
}
}
}
const receiveMessage = function () {
const message = arguments[0]
operations[message].apply(this, Array.prototype.slice.call(arguments, 1))
}
return {
receiveMessage
}
})()
复制代码
经过添加中介者,玩家与玩家之间的耦合关系已经解除,某个玩家的操做不须要通知其它玩家,只要给中介者发送一条消息,中介者处理完消息以后再把结果反馈给其它玩家。
测试代码:
const player1 = playerFactory('A', 'red')
const player2 = playerFactory('B', 'red')
const player3 = playerFactory('C', 'red')
const player4 = playerFactory('D', 'red')
const player5 = playerFactory('E', 'blue')
const player6 = playerFactory('F', 'blue')
const player7 = playerFactory('G', 'blue')
const player8 = playerFactory('H', 'blue')
player1.die()
player2.die()
player3.die()
player4.die()
复制代码
获得结果:
中介者模式是迪米特法则的一种实现,迪米特法则也叫最少知识原则,是指一个对象应该尽量少了解其它的对象。若是对象之间耦合性过高,一个对象的变化将会影响其它对象,在中介者模式中,对象之间几乎不知道彼此的存在,它们之间经过中介者来相互影响。 中介者模式也存在一些缺点,最大的缺点是系统中会新增一个巨大的中介者对象,由于中介者对象之间交互的复杂性,所有转移到中介者对象上,因此维护好中介者也是很困难的事。中介者模式能够很方便地解耦对象之间的关系,可是对象之间的关系并不必定须要解耦,因此在写代码时须要权衡对象之间的耦合程度。通常来讲,若是对象之间的复杂耦合确实致使了调用和维护困难,并且这些耦合会随着项目的变化还在继续增长,咱们就能够考虑使用中介者模式来重构代码。