中介者模式
2022年9月29日大约 6 分钟
中介者模式
中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。
中介者模式的例子——泡泡堂游戏
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 对象,一般有以下两种方式。
// 1. 利用发布—订阅模式。将 playerDirector 实现为订阅者,各 player 作为发布者,一旦 player的状态发生改变,便推送消息给 playerDirector,playerDirector 处理消息后将反馈发送给其他 player。
// 2. 在 playerDirector 中开放一些接收消息的接口,各 player 可以直接调用该接口来给playerDirector 发送消息,player 只需传递一个参数给 playerDirector,这个参数的目的是使 playerDirector 可以识别发送者。同样,playerDirector 接收到消息之后会将处理结果反馈给其他 player。
// 这两种方式的实现没什么本质上的区别。在这里我们使用第二种方式,playerDirector 开放一个对外暴露的接口 reciveMessage,负责接收 player 对象发送的消息,而 player 对象发送消息的时候,总是把自身 this 作为参数发送给 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; // 改变队伍颜色
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); // arguments 的第一个参数为消息名称
operations[message].apply(this, arguments);
};
return {
reciveMessage: reciveMessage
}
})();
// 可以看到,除了中介者本身,没有一个玩家知道其他任何玩家的存在,玩家与玩家之间的耦合关系已经完全解除,某个玩家的任何操作都不需要通知其他玩家,而只需要给中介者发送一个消息,中介者处理完消息之后会把处理结果反馈给其他的玩家对象。我们还可以继续给中介者扩展更多功能,以适应游戏需求的不断变化。
// 我们来看下测试结果:
// 红队:
var player1 = playerFactory('皮蛋', 'red'),
player2 = playerFactory('小乖', 'red'),
player3 = playerFactory('宝宝', 'red'),
player4 = playerFactory('小强', 'red');
// 蓝队:
var player5 = playerFactory('黑妞', 'blue'),
player6 = playerFactory('葱头', 'blue'),
player7 = playerFactory('胖墩', 'blue'),
player8 = playerFactory('海盗', 'blue');
//红队全部死亡
player1.die();
player2.die();
player3.die();
player4.die();
// 皮蛋 lost
// 小乖 lost
// 宝宝 lost
// 小强 lost
// 黑妞 won
// 葱头 won
// 胖墩 won
// 海盗 won
// 假设皮蛋和小乖掉线
// player1.remove();
// player2.remove();
// player3.die();
// player4.die();
// // 宝宝 lost
// // 小强 lost
// // 黑妞 won
// // 葱头 won
// // 胖墩 won
// // 海盗 won
// 假设皮蛋从红队叛变到蓝队
// player1.changeTeam( 'blue' );
// player2.die();
// player3.die();
// player4.die();
中介者模式的例子——购买商品
<body>
选择颜色:
<select id="colorSelect">
<option value="">请选择</option>
<option value="red">红色</option>
<option value="blue">蓝色</option>
</select>
选择内存:
<select id="memorySelect">
<option value="">请选择</option>
<option value="32G">32G</option>
<option value="16G">16G</option>
</select>
输入购买数量: <input type="text" id="numberInput"/><br/>
您选择了颜色: <div id="colorInfo"></div><br/>
您选择了内存: <div id="memoryInfo"></div><br/>
您输入了数量: <div id="numberInfo"></div><br/>
<button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>
</body>
<script>
var goods = { // 手机库存
"red|32G": 3,
"red|16G": 0,
"blue|32G": 1,
"blue|16G": 6
};
var mediator = (function () {
var colorSelect = document.getElementById('colorSelect'),
memorySelect = document.getElementById('memorySelect'),
numberInput = document.getElementById('numberInput'),
colorInfo = document.getElementById('colorInfo'),
memoryInfo = document.getElementById('memoryInfo'),
numberInfo = document.getElementById('numberInfo'),
nextBtn = document.getElementById('nextBtn');
return {
changed: function (obj) {
var color = colorSelect.value,
// 颜色
memory = memorySelect.value,// 内存
number = numberInput.value,
// 数量
stock = goods[color + '|' + memory]; // 颜色和内存对应的手机库存数量
if (obj === colorSelect) {
// 如果改变的是选择颜色下拉框
colorInfo.innerHTML = color;
} else if (obj === memorySelect) {
memoryInfo.innerHTML = memory;
} else if (obj === numberInput) {
numberInfo.innerHTML = number;
}
if (!color) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择手机颜色';
return;
}
if (!memory) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择内存大小';
return;
}
if (((number - 0) | 0) !== number - 0) { // 输入购买数量是否为正整数
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车';
}
}
})();
// 事件函数:
colorSelect.onchange = function () {
mediator.changed(this);
};
memorySelect.onchange = function () {
mediator.changed(this);
};
numberInput.oninput = function () {
mediator.changed(this);
};
// 可以想象,某天我们又要新增一些跟需求相关的节点,比如 CPU 型号,那我们只需要稍稍改动 mediator 对象即可:
var goods = {
// 手机库存
"red|32G|800": 3,
// 颜色 red,内存 32G,cpu800,对应库存数量为 3
"red|16G|801": 0,
"blue|32G|800": 1,
"blue|16G|801": 6
};
var mediator = (function () {
// 略
var cpuSelect = document.getElementById('cpuSelect');
return {
change: function (obj) {
// 略
var cpu = cpuSelect.value,
stock = goods[color + '|' + memory + '|' + cpu];
if (obj === cpuSelect) {
cpuInfo.innerHTML = cpu;
}
// 略
}
}
})();
</script>
小结
中介者模式使各个对象之间得以解耦,以中介者和对象之间的一对多关系取代了对象之间的网状多对多关系。各个对象只需关注自身功能的实现,对象之间的交互关系交给了中介者对象来实现和维护。
不过,中介者模式也存在一些缺点。其中,最大的缺点是系统中会新增一个中介者对象,因为对象之间交互的复杂性,转移成了中介者对象的复杂性,使得中介者对象经常是巨大的。中介者对象自身往往就是一个难以维护的对象。
我们都知道,毒贩子虽然使吸毒者和制毒者之间的耦合度降低,但毒贩子也要抽走一部分利润。同样,在程序中,中介者对象要占去一部分内存。而且毒贩本身还要防止被警察抓住,因为它了解整个犯罪链条中的所有关系,这表明中介者对象自身往往是一个难以维护的对象。