中介者模式

中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系

故事背景

假如在玩泡泡堂的游戏,使用泡泡击败对方所有玩家才能获得胜利。现在将队伍分成两个组进行游戏

代码实现(未使用中介者模式)

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
64
65
66
67
68
69
70
// 玩家
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 () { // 玩家死亡
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();
}
}
};

// 定义一个工厂来创建玩家
let playerFactory = function (name, teamColor) {
let 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;
};

let players = [];
//红队:
let player1 = playerFactory("皮蛋", "red"),
player2 = playerFactory("小乖", "red"),
player3 = playerFactory("宝宝", "red"),
player4 = playerFactory("小强", "red");
//蓝队:
let player5 = playerFactory("黑妞", "blue"),
player6 = playerFactory("葱头", "blue"),
player7 = playerFactory("胖墩", "blue"),
player8 = playerFactory("海盗", "blue");

// 让红队玩家全部死亡:
player1.die();
player2.die();
player4.die();
player3.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
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// 玩家
function Player(name, teamColor) {
this.state = "live"; // 玩家状态
this.name = name; // 角色名字
this.teamColor = teamColor; // 队伍颜色
}

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); // 给中介者发送消息,玩家换队
};

/*******************定义中介者对象*****************/
let playerDirector = (function () {
let players = {}, // 保存所有玩家
operations = {}; // 中介者可以执行的操作
/****************新增一个玩家***************************/
operations.addPlayer = function (player) {
let teamColor = player.teamColor; // 玩家的队伍颜色
players[teamColor] = players[teamColor] || []; // 如果该颜色的玩家还没有成立队伍,则新成立一个队伍
players[teamColor].push(player); // 添加玩家进队伍
};
/****************移除一个玩家***************************/
operations.removePlayer = function (player) {
let teamColor = player.teamColor, // 玩家的队伍颜色
teamPlayers = players[teamColor] || []; // 该队伍所有成员
for (let 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) { // 玩家死亡
let teamColor = player.teamColor,
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 === true) {// 全部死亡
for (let i = 0, player; player = teamPlayers[i++];) {
player.lose(); // 本队所有玩家 lose
}
for (let color in players) {
if (color !== teamColor) {
let teamPlayers = players[color]; // 其他队伍的玩家
for (let i = 0, player; player = teamPlayers[i++];) {
player.win(); // 其他队伍所有玩家 win
}
}
}
}
};

let receiveMessage = function () {
let message = Array.prototype.shift.call(arguments);
operations[message].apply(this, arguments);
};
return {
receiveMessage: receiveMessage,
};
})();

/*******************设置工厂函数*****************/
let playerFactory = function (name, teamColor) {
let newPlayer = new Player(name, teamColor); // 创造一个新的玩家对象
playerDirector.receiveMessage("addPlayer", newPlayer); // 给中介者发送消息,新增玩家
return newPlayer;
};

//红队
let player1 = playerFactory("皮蛋", "red"),
player2 = playerFactory("小乖", "red"),
player3 = playerFactory("宝宝", "red"),
player4 = playerFactory("小强", "red");
// 蓝队:
let player5 = playerFactory("黑妞", "blue"),
player6 = playerFactory("葱头", "blue"),
player7 = playerFactory("胖墩", "blue"),
player8 = playerFactory("海盗", "blue");

player1.die();
player2.die();
player3.die();
player4.die();

现在玩家与玩家之间的耦合消除了,所有操作逻辑在中介者对象中实现,玩家只需要向中介者发生消息,中介者会把处理结果反馈给其他玩家对象。

使用时机

中介者模式可以非常方便地对模块或者对象进行解耦,但对象之间并非一定需要解耦。在实际项目中,模块或对象之间有一些依赖关系是很正常的。毕竟我们写程序是为了快速完成项目交付生产,而不是堆砌模式和过度设计。关键就在于如何去衡量对象之间的耦合程度。一般来说, 如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长曲线,那我们就可以考虑用中介者模式来重构代码。

小结

中介者模式是迎合迪米特法则的一种实现。迪米特法则也叫最少知识原则,是指一个对象应该尽可能少地了解另外的对象。如果对象之间的耦合性太高,一个对象发生改变之后,难免会影响到其他的对象,而在中介者模式里,对象之间几乎不知道彼此的存在,它们只能通过中介者对象来互相影响对方。

中介者模式也存在一些缺点。其中最大的缺点是系统中会新增一个中介者对象,因 为对象之间交互的复杂性,转移成了中介者对象的复杂性,使得中介者对象经常是巨大的。中介者对象自身往往就是一个难以维护的对象。

参考

《javascript设计模式与开发实践》

作者

Liang

发布于

2021-02-08

更新于

2021-12-25

许可协议


评论