[TOC]
# 中介者模式
解除對象與對象之間的緊耦合關系。增加一個中介者對象后,所有的相關對象都通過中介者對象來通信,而不是互相引用,所有當一個對象發生改變時,只需要通知中介者對象即可。中介者使各對象之間耦合松散,而且可以獨立地改變它們之間的交互。中介者模式使網狀的多對多關系變成了相對簡單的一對多關系。
## 中介者模式的例子---泡泡堂游戲
改造前:
```javascript
// 定義數組保存所有玩家
var players = [];
// 先定義玩家構造函數
function Player(name, teamColor){
this.partners = []; // 隊友列表
this.enemies = []; // 敵人列表
this.state = 'live'; // 玩家狀態
this.name = name; // 角色名字
this.teamColor = null; // 隊伍顏色
};
// 玩家團隊勝利
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.partners[i++];){
if(partner.state !== 'dead'){
all_dead = false;
break;
}
}
// 如果隊友全部死亡
if(all_dead === true){
this.lose();
// 通知所有隊友玩家游戲失敗
for(var i=0, partner; partner = this.partners[i++];){
partner.lose();
}
// 通知所有敵人游戲勝利
for(var i=0, enemy; enmy = this.enemise[i++];){
enemy.win();
}
}
};
// 定義一個工廠來創建玩家
var playerFactory = function(name, teamColor){
// 創建新玩家
var newPlayer = new Player(name, teamColor);
// 通知所有玩家有新角色加入
for(var i=0,player; player=players[i++];){
if(player.teamColor === newPlayer.teamColor){ // 同一隊玩家
player.partners.push(newPlayer); // 互相添加到隊友列表
newPlayer.partner.push(player);
}else{
player.enemies.push(newPlayer); // 互相添加到敵人列表
newPlayer.enemies.push(player);
}
}
players.push(newPlayer);
return newPlayer;
};
// 創建8個玩家對象
var player1 = playerFactory('a', 'red');
var player2 = playerFactory('b', 'red');
var player3 = playerFactory('c', 'red');
var player4 = playerFactory('d', 'red');
var player5 = playerFactory('e', 'blue');
var player6 = playerFactory('f', 'blue');
var player7 = playerFactory('g', 'blue');
var player8 = playerFactory('h', 'blue');
```
這里每個玩家和其它玩家都是緊緊耦合在一起的。用中介者模式進行改造。
```javascript
function Player(name, teamColor){
this.name = name; // 角色名字
this.teamColor = teamColor; // 隊伍顏色
this.state = 'alive'; // 玩家生存狀態
}
Player.prototype.win = function(){
console.log('winner:' + this.name);
};
Player.prototype.lose = function(){
console.log('loser:' + this.name);
};
Player.prototype.die = function(){
this.state = 'dead';
playerDirector.ReceiveMessage('playerDead', this); // 給中介者發送消息,玩家死亡
};
Player.prototype.remove = function(){
playerDirector.ReceiveMessage('removePlayer', this); // 給中介者發送消息,移除一個玩家
};
Player.prototype.changeTeam = function(){
playerDirector.ReceiveMessage('changeTeam', this); // 給中介者發送消息,玩家換隊
};
var playerFactory = function(name, teamColor){
var newPlayer = new Player(name, teamColor);
playerDirector.ReceiveMessage('addPlayer', newPlayer); // 給中介者發送消息,新增玩家
return newPlayer;
};
// 實現playerDirector對象
var playDirector = (function(){
var players = {}; // 保存所有玩家
var operations = {}; // 中介者可以執行的操作
// 新增一個玩家
operations.add = function(player){
var teamColor = player.teamColor;
players[teamColor] = players[teamColor] || [];
players[teamColor].push(player);
};
// 移除一個玩家
operations.removePlayer = function(player){
var teamColor = player.teamColor;
var 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;
var teamPlayer = players[teamColor];
var all_dead = true;
// 遍歷隊友列表
for(var i=0, player; player = teamPlayer[i++];){
if(player.state !== 'dead'){
all_dead = false;
break;
}
}
// 如果隊友全部死亡
if(all_dead === true){
this.lose();
// 通知所有隊友玩家游戲失敗
for(var i=0, player; player = teamPlayer[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 ReceiveMessage = function(){
var message = Array.prototype.shift.call(arguments);
operations[message].apply(this, arguments);
};
return {
ReciveMessage: ReceiveMessage
}
})();
// 創建8個玩家對象
var player1 = playerFactory('a', 'red');
var player2 = playerFactory('b', 'red');
var player3 = playerFactory('c', 'red');
var player4 = playerFactory('d', 'red');
var player5 = playerFactory('e', 'blue');
var player6 = playerFactory('f', 'blue');
var player7 = playerFactory('g', 'blue');
var player8 = playerFactory('h', 'blue');
```
中介者模式是迎合迪米特法則的一種實現。迪米特法則也叫最少知識原則,是指一個對象應該盡可能少地了解另外的對象(類似不合陌生人說話)。如果對象之間的耦合性太高,一個對象發生改變之后,難免會影響到其他的對象。而在中介者模式里,對象之間幾乎不知道彼此的存在,它們只能通過中介者對象來互相影響對方。
中介者模式最大的缺點是系統中會新增一個中介者對象,因為對象之間交互的復雜性,轉移成了中介者對象的復雜性,使得中介者對象經常是巨大的。中介者對象自身往往就是一個難以維護的對象。
寫程序是為了快速完成項目交付生產,而不是堆砌模式和過渡設計。關鍵就在于如何去衡量對象之間的耦合程度。如果對象之間的復雜耦合確實導致調用和維護出現了困難,而且這些耦合度隨項目的變化呈指數增長曲線,那就可以考慮用中介者模式來重構代碼。