JavaScript设计模式十二(中介者模式)
定义:
中介者模式的作用就是解除对象和对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者来通信,而不是相互引用,所以当一个对象发生改变时,只需要通知中介者对象就可以了。让之前的网状关系变成了一对多的关系
现实生活中的中介者
显示生活中有很多中介者的例子,例如
- 机场指挥塔
中介者也称为调停者,如果没有机场的指挥塔存在,每一架飞机都要和方圆100公里内的所有飞机通信,才能确定航线和飞行状况,这是难以想象的。显示中的情况就是,每架飞机起飞或者降落的时候和指挥塔通信,指挥塔知道每架飞机的情况和跑道的情况,他可以合理的做出决策 - 博彩公司
在世界杯期间买足球彩票,如果没有博彩公司作为中介,上千万的人一起计算赔率和输赢是不可能的事情啊,有了博彩公司作为中介,每个人只需要和博彩公司发生关联就可以了,输赢都去找他们
中介者例子–泡泡堂游戏
我们先看最简单的例子,游戏只支持两个玩家同时进行对战,定义一个构造函数和3个方法win、close和die
function Player(name) {
this.name = name;
this.enemy = null;
}
Player.prototype.win = function() {
console.log(this.name + 'won');
}
Player.prototype.lose = function() {
console.log(this.name + 'lost');
}
Play.prototype.die = function() {
this.lose();
this.enemy.win();
}
var player1 = new Player('player1');
var palyer2 = new Player('player2');
player1.enemy = player2;
palyer2.enemy = player1;
当palyer1被炸死的时候,只需要调用player1.die()就可以了,但是实际上没这么简单,玩过泡泡堂的都知道,游戏里面之多可以有8个玩家,并且分为红蓝两队进行游戏。
我们定义一个数组players来保存所有玩家,在创建玩家之后,循环players来给每个玩家设置队友和敌人。
function Player(name, teamColor) {
this.partners = [];
this.enemies = [];
this.state = 'live';
this.name = name;
this.teamColor = teamColor;
}
失败或者成功后还是很简单,直接输出就可以了
Player.prototype.win = function() {
console.log('winner: ' + this.name);
}
Player.prototype.lose = function() {
console.log('loser: ' + this.name);
}
玩家死亡的方法需要复杂点,每个玩家死亡的时候,都遍历一遍其他队友的情况,如果队友都死了,则这句游戏失败,同时敌人队伍的所有玩家都取得胜利
Player.prototype.die = function() {
var all_dead = true;
this.state = 'dead';
for (var i = 0, partner; partner = this.partner[i++]) {
if (partner.state !== 'dead') {
all_dead = false;
break;
}
}
if (all_dead === true) {
this.lose();
for(var i = 0, partner; partner= this.partner[i++]) {
partner.lose();
}
for(var i = 0, enemy; enemy = this.enemies[i++]) {
enemy.win();
}
}
}
然后我们用一个工厂来创建玩家
var playerFactory = function(name, teamColor) {
var newPlayer = new Player(name, teamColor);
for (var i = 0, palyer; player = player[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;
}
创建玩家:
var player1 = playerFactory('qqq', 'red');
var player2 = playerFactory('www', 'red');
var player3 = playerFactory('eee', 'red');
var palyer4 = playerFactory('rrr', 'red');
var player5 = playerFactory('aaa', 'blue');
var player6 = playerFactory('sss', 'blue');
var player7 = playerFactory('ddd', 'blue');
var player8 = playerFactory('fff', 'blue');
红队都阵亡:
player1.die();
player2.die();
player3.die();
player4.die();
我们考虑下,上面代码还有什么问题?每个玩家和其他玩家都是紧密耦合在一起的,在这段代码中,每个玩家都有两个属性this.partners和this.enemies,用来保存其他玩家对象的引用,每次有角色死亡或者移动就要显示的遍历通知所有的其他对象。
这里只有8个,想想如果是80、800,或者玩家可以叛变,这就不是循环能解决的问题了。
接下来我们用中介者模式来改造泡泡堂游戏
首先定义Player构造函数和player对象的原型方法,在palyer对象的这些原型方法中,不再负责具体的逻辑,而是把操作转交给中介者对象,我们命名为playerDirector
function Player(name, teamColor) {
this.name = name;
this.teamColor = teamColor;
this.state = 'alive';
}
Player.prototype.win = function() {
console.log(this.name + ' won ');
}
Player.prototype.lose = function() {
console.log(this.name + ' lost ');
}
Player.prototype.die = function() {
this.state = 'dead';
playerDirector.receiveMessage('playerDead', this);
}
Player.prototype.remove = function() {
playerDirector.receiveMessage('removePlayer', this);
}
Player.prototype.changeTeam = function(color) {
playerDirector.receiveMessage('changeTeam', this, color);
}
var playFactory = function(name, teamColor) {
var newPlayer = new Player(name, teamColor);
playerDirector.receiveMessage('addPlayer', newPlayer);
return newPlayer;
}
接下来就是我们如何来实现中介者playerDirector对象,一般来说有两种方式:
- 发布订阅模式,playerDirector是订阅者,palyer是发布者,一旦player发生改变,就推送消息给playerDirector,然后中介者处理消息后反馈给其他player
- 在playerDirector中开发一些接受消息的接口,各个player可以调用这个接口来给playerDirector发送消息,player传递参数给playerDirector,参数的目的是让playerDirector识别发送者。
这两种方式实现本质上没有区别
var playerDirector = (function() {
var players = {},
operations = {};
operations.addPlayer = function(player) {
var teamColor = player.teamColor;
players[teamColor] = players[teamColor] || [];
players[teamColor].push(player);
};
operations.removePlayer = function(player) {
var teamColor = player.teamColor,
teamPlayers = players[teamColor] || [];
for (var i = teamPlayers.length - 1; i >=0; i--) {
if (teamPlayers[i] === player) {
teamPlayers.splice(i, 1);
}
}
};
operations.changeTeam = function(player, newTeamColor) {
operations.removePlayer(player);
player.teamColor = newTeamColor;
opeations.addPlayer(player);
};
operations.playerDead = function(player) {
var teamColor = player.teamColor,
teamPlayers = palyers[teamColor];
var all_dead = true;
for (var i = 0, player; player = teamPlayers[i++]; ){
if (player.state !== 'dead') {
all_dead = false;
break;
}
}
if (all_dead === true) {
for (var i = 0, palyer; player = teamPlayers[i++]; ){
player.lose();
}
for (var color in players) {
if (color !== teamColor) {
var teamPlayers = players[color];
for (var i = 0, player; player = teamPlayers[i++]; ) {
player.win();
}
}
}
}
};
var recieveMessage = function() {
var message = Array.prototype.shift.call(arguments);
operations[message].apply(this, arguments);
};
return {
receiveMessage: recieveMessage
};
})();
我们可以看到,除了中介者本身,没有一个玩家是知道其他任何玩家的存在的,玩家和玩家之间耦合关系基本没有,中介者还可以根据游戏的需要增加更多的功能
小结
中介者模式是最少知识原则的一种实现,是指一个对象应该尽可以能少的了解另外的对象。如果对象的耦合性太高,一个对象的改变难免会影响到其他的对象。
中介者最大的缺点在于系统中会增加一个中介者对象,因为对象之间的交互的复杂性,转移成为了中介者对象的复杂性,是的中介者对象可能很庞大,难以维护。
一般来说,如果对象之间的复杂耦合导致调用和维护出现困难,而且这些耦合度随着项目的变化呈指数增加曲线的时候,我们就可以考虑用中介者模式来重构代码。