众所周知,在程序中,程序是由大大小小的单一对象组成的。所有这些对象都按照某种关系和规则来通信 —— 引用。
这导致了一些问题:当程序越来越大,对象越来越多,他们之间的关系也越来越复杂。有时甚至就会形成交叉的网状结构。这样一来,当我们改变或删除其中一个对象时,有可能需要通知所有引用到它的对象…
【中介者模式】的作用就是解除对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用。所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。简单来说,中介者模式使网状的多对多关系变成了简单的一对多关系:
笔者感觉【中介者模式】和【发布-订阅模式】、vue中的【Bus总线机制】都有异曲同工之妙。它比发布-订阅多了一个“中介”,又比Bus多了一个“对象集合”;
中介者模式的存在,很大程度上解除了对象之间的紧耦合关系,而且不必再为“增加队友”时的性能和效率问题而发愁。
…它们仅仅通知中介者它们改变了,同时把自身作为参数传进去,以便中介者辨别是谁发生了改变。剩下的事情都交给中介者对象来完成。这样一来,无论修改还是新增,都只需改动中介者对象里的代码。
你是否知道曾经风靡一时的游戏:泡泡堂?多人对战、组队、叛变…
我们用【中介者模式】来模拟一下主要功能:
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.reciveMessage( 'playerDead', this ); // 给中介者发送消息,玩家死亡
};
//移除玩家
Player.prototype.remove = function(){
playerDirector.reciveMessage( 'removePlayer', this ); // 给中介者发送消息,移除一个玩家
};
//玩家换队
Player.prototype.changeTeam = function( color ){
playerDirector.reciveMessage( 'changeTeam', this, color ); // 给中介者发送消息,玩家换队
};
//工厂函数
var playerFactory = function( name, teamColor ){
var newPlayer = new Player( name, teamColor ); // 创造一个新的玩家对象
playerDirector.reciveMessage( 'addPlayer', newPlayer ); // 给中介者发送消息,新增玩家
return newPlayer;
};
对于中介者(playerDirector),这里我们采用“接口”的方式 —— ,playerDirector开放一个对外暴露的接口 reciveMessage,负责接收 player 对象发送的消息,并处理:
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; // 改变队伍颜色
operations.addPlayer( player ); // 增加到新队伍中
};
operations.playerDead = function( player ){ // 玩家死亡
var teamColor = player.teamColor,
teamPlayers = players[ 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, player; player = teamPlayers[ i++ ]; ){
player.lose(); // 本队所有玩家 lose
}
for ( var color in players ){
if ( color !== teamColor ){
var teamPlayers = players[ color ]; // “其他队伍”的玩家
for ( var i = 0, player; player = teamPlayers[ i++ ]; ){
player.win(); // “其他队伍”所有玩家 win
}
}
}
}
};
var reciveMessage = function(){
var message = Array.prototype.shift.call( arguments );
operations[ message ].apply( this, arguments );
};
return {
reciveMessage //reciveMessage:reciveMessage 暴露接口
}
})();
可以看到,除了中介者本身,没有一个玩家知道其他任何玩家的存在,玩家与玩家之间的耦合关系已经完全解除,某个玩家的任何操作都不需要通知其他玩家,而只需要给中介者发送一个消息,中介者处理完消息之后会把处理结果反馈给其他的玩家对象。我们还可以继续给中介者扩展更多功能,以适应游戏需求的不断变化。