中介者模式
中介者模式的定义:
中介者模式的作用就是解除对象与对象之间的耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用。中介者使各个对象之间的耦合松散,而且可以独立地改变他们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。
举个例子 —— 泡泡堂游戏
定义一个玩家类,它包含三个方法win
、lose
和die
,当设定的玩家数目为2名时,其中一位玩家死亡则游戏结束,同时通知它的对手胜利:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Player { constructor (name) { this.name = name this.enemy = null } win () { console.log('winner: ' + this.name) } lose () { console.log('loser: ' + this.name) } die () { this.lose() this.enemy.win() } }
|
1 2 3 4 5 6 7
| const player1 = new Player('one') const player2 = new Player('two')
player1.enemy = player2 player2.enemy = player1
player1.die()
|
▼游戏玩法升级
此时,我们想让玩家的数量增加到8个人,并且分成两个小队,因此我们需要改造我们的Player
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class Player { constructor (name, teamColor) { this.partners = [] this.enemies = [] this.state = 'alive' this.name = name this.teamColor = teamColor } win () { console.log('winner: ' + this.name) } lose () { console.log('loser: ' + this.name) } 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) { this.lose() for (let i = 0, partner; partner = this.partners[ i++ ];) { partner.lose() } for (let i = 0, enemy; enemy = this.enemies[ i++ ];) { enemy.win() } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const playerFactory = (function () { const players = [] return function (name, teamColor) { var 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 } })()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const player1 = playerFactory('1', 'red') const player2 = playerFactory('2', 'red') const player3 = playerFactory('3', 'red') const player4 = playerFactory('4', 'red')
const player5 = playerFactory('5', 'blue') const player6 = playerFactory('6', 'blue') const player7 = playerFactory('7', 'blue') const player8 = playerFactory('8', 'blue')
player1.die() player2.die() player3.die() player4.die()
|
现在我们已经可以随意地为游戏增加玩家和队伍,但是由于玩家与玩家之间都是紧密耦合在一起的,当有玩家死亡时,都必须显示地遍历通知其他对象。在例子中其实还不难操作,但是如果是一个有成百上千人的玩家队伍中,有一个玩家掉线,那么就必须遍历其他所有玩家的队友列表和敌人列表去移除这位玩家。又或者其中一位玩家从红队变成蓝队,这样的操作都会使得代码变得特别的复杂。
▼使用中介者模式重构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Player { constructor (name, teamColor) { this.name = name this.teamColor = teamColor this.state = 'alive' } win () { console.log('winner: ' + this.name) } lose () { console.log('loser: ' + this.name) } die () { this.state = 'dead' playerDirector.reciveMessage('playerDead', this) } remove () { playerDirector.reciveMessage('removePlayer', this) } changeTeam (color) { playerDirector.reciveMessage('changeTeam', this, color) } }
|
1 2 3 4 5 6
| const playerFactory = function (name, teamColor) { const newPlayer = new Player(name, teamColor) playerDirector.reciveMessage('addPlayer', newPlayer) return newPlayer }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| const playerDirector = (function () { const players = {} const operatons = {} operatons.addPlayer = function (player) { const teamColor = player.teamColor players[ teamColor ] = players[ teamColor ] || [] players[ teamColor ].push(player) } operatons.removePlayer = function (player) { const teamColor = player.teamColor const teamPlayers = players[ teamColor ] || [] for (let i = teamPlayers.length - 1; i >= 0; i--) { if (teamPlayers[i] === player) { teamPlayers.splice(i, 1) } } } operatons.changeTeam = function (player, newTeamColor) { operatons.removePlayer(player) player.teamColor = newTeamColor operatons.addPlayer(player) } operatons.playerDead = function (player) { const teamColor = player.teamColor const teamPlayers = players[ teamColor ] || [] let all_dead = true for (let i = 0, player; player = teamPlayers[ i++ ];) { if (player.state !== 'dead') { all_dead = false break } } if (all_dead) { for (let i = 0, player; player = teamPlayers[ i++ ];) { player.lose() } for (let color in players) { if (color !== teamColor) { const teamPlayers = players[color] for (let i = 0, player; player = teamPlayers[ i++ ];) { player.win() } } } } } var reciveMessage = function () { var message = Array.prototype.shift.call(arguments) operatons[message].apply(this, arguments) } return { reciveMessage } })()
|
可以看到,除了中介者本身,没有一个玩家知道其他任何玩家的存在,玩家与玩家之间的耦合关系完全解除,某个玩家的任何操作都不需要通知其他玩家,而只需要给中介者发送一个消息,中介者处理完消息之后会把处理的结果反馈给其他玩家对象。我们还可以继续给中介者扩展更多的功能,以适应游戏需求的不断变化。
总结
优缺点
- 优点:
- 使各个对象之间得以解耦,,以中介者和对象之间的一对多关系取代了对象之间的网状多对多关系
- 使得对象只需要关注自身功能的实现
- 对象之间的交互关系交给了中介者对象来实现和维护
- 缺点:
- 系统中会新增一个中介者对象,原本对象之间交互的复杂性会转移成中介者对象的复杂性,使得中介者对象十分巨大难以维护
中介者模式可以非常方便地对模块或对象进行解耦,但对象之间并非一定需要解耦。在实际项目中,模块或对象之间有一些以来关系是很正常的。因此可以看情况使用中介者模式。
文章参考:
《JavaScript设计模式与开发实践》