中介者模式的作用就是接触对象与对象之间的紧耦合关系。增加一个中介者对象后,所有相关对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变他们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多的关系。
javascript
// 泡泡堂游戏
// 先定义一个玩家类,有三个方法win,lose,die,
// 玩家数目2个,当其中一个玩家死亡游戏便结束,同时通知对手胜利
class Player {
constructor(name){
this.name=name;
this.enemy=null; // 敌人
}
win(){
console.log(this.name+'won');
}
lose(){
console.log(this.name+'lost');
}
die(){
this.lose();
this.enemy.win();
}
}
// 接下来创建2个玩家对象
const player1 = new Player('皮蛋');
const player2 = new Player('小乖');
// 给玩家相互设置敌人
player1.enemy=player2;
player2.enemy=player1;
player1.die(); // 输出皮蛋lost 小乖won
javascript
// 为游戏增加队伍
const players = [];
class Player {
constructor(name,teamColor){
this.partners=[]; // 队友列表
this.enemies=[]; //敌人列表
this.state='live'; //玩家状态
this.name=name; // 角色名字
this.teamColor = teamColor; // 队伍颜色
}
win(){ // 玩家团队胜利
console.log('winner: '+this.name);
}
lose(){ // 玩家团队失败
console.log('loser: '+this.name);
}
die(){ // 玩家死亡
var 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();
}
}
}
}
// 最后定义一个工厂来创建玩家
const playerFactory = function(name,teamColor){
const 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;
}
// 现在来感受一下,用这段代码创建8个玩家
// 红队:
const player1=playerFactory('皮蛋','red');
const player2=playerFactory('小乖','red');
const player3=playerFactory('宝宝','red');
const player4=playerFactory('小强','red');
// 蓝队:
const player5=playerFactory('黑妞','blue');
const player6=playerFactory('葱头','blue');
const player7=playerFactory('胖墩','blue');
const player8=playerFactory('海盗','blue');
console.log(players)
// 让红队玩家全部死亡
player1.die();
player2.die();
player3.die();
player4.die();
在上面这段代码中,每个玩家都有两个属性,this.partners和this.enemies,用来保存其他玩家对象的引用,当每个对象的状态发生改变,比如角色移动、吃到道具或者死亡,都必须显式地遍历通知其他对象。这个例子只有8个对象,如果有成千个玩家,几十支队伍湖乡厮杀,有一个玩家掉线,必须从所有其他玩家的队友列表和敌人列表中都移除这个玩家。或许还有解除队伍和添加到别的队伍的功能,红色玩家可以突然变成蓝色玩家,这不仅仅是循环能解决的问题了,下面用中介者模式来改造上面的代码
javascript
// 泡泡堂游戏
class Player {
constructor(name,teamColor){
this.state='alive'; //玩家生存状态
this.name=name; // 角色名字
this.teamColor = teamColor; // 队伍颜色
}
win(){ // 玩家胜利
console.log(this.name+' won');
}
lose(){ // 玩家失败
console.log(this.name+' lost');
}
die(){ // 玩家死亡
this.state='dead';
playerDirector.ReceiveMessage('playerDead',this); //给中介者发送消息,玩家死亡
}
remove(){ // 移除玩家
playerDirector.ReceiveMessage('removePlayer',this); // 给中介者发送消息,移除一个玩家
}
changeTeam(color){ // 玩家换队
playerDirector.ReceiveMessage('changeTeam',this,color); // 给中介者发送消息,玩家换队
}
}
// 最后定义一个工厂来创建玩家
const playerFactory = function(name,teamColor){
const newPlayer = new Player(name,teamColor); // 创建新玩家
playerDirector.ReceiveMessage('addPlayer',newPlayer);
return newPlayer;
}
// 实现中介者对象playerDirector
const playerDirector=(function(){
const players={}; // 保存所有玩家
const 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; // 玩家队伍颜色
let 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;
let 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
}
}
}
}
};
const ReceiveMessage = function(){
let message = Array.prototype.shift.call(arguments); // 取出arguments的第一个参数
operations[message].apply(this,arguments);
};
return {
ReceiveMessage:ReceiveMessage
}
})();
// 红队:
const player1=playerFactory('皮蛋','red');
const player2=playerFactory('小乖','red');
const player3=playerFactory('宝宝','red');
const player4=playerFactory('小强','red');
// 蓝队:
const player5=playerFactory('黑妞','blue');
const player6=playerFactory('葱头','blue');
const player7=playerFactory('胖墩','blue');
const player8=playerFactory('海盗','blue');
// 让红队玩家全部死亡
// player1.die();
// player2.die();
// player3.die();
// player4.die();
// 假设皮蛋和小乖掉线
// player1.remove();
// player2.remove();
// player3.die();
// player4.die();
// 假设皮蛋从红队叛变到蓝队
player1.changeTeam('blue');
player2.remove();
player3.die();
player4.die();
购买商品的demo
html
<body>
选择颜色:<select id="colorSelect">
<option value="">请选择</option>
<option value="red">红色</option>
<option value="blue">蓝色</option>
</select>
输入购买数量:<input type="text" id="numberInput">
您选择了颜色:<div id="colorInfo"></div><br />
您输入了数量:<div id="numberInfo"></div><br />
<button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>
<script>
var colorSelect = document.getElementById('colorSelect');
var numberInput = document.getElementById('numberInput');
var colorInfo = document.getElementById('colorInfo');
var numberInfo = document.getElementById('numberInfo');
var nextBtn = document.getElementById('nextBtn');
var goods = { // 手机库存
'red': 3,
'blue': 6
};
colorSelect.onchange = function () {
var color = this.value; // 颜色
var number = numberInput.value; // 数量
var stock = goods[color]; // 该颜色手机对应的当前库存
colorInfo.innerHTML = color;
if (!color) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择手机颜色';
return;
}
if (((number - 0) | 0) !== number - 0) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
if (number > stock) {
nextBtn.disabled = true;
nextBtn.innerHTML = '库存不足';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车'
}
numberInput.oninput = function () {
var color = colorSelect.value; // 颜色
var number = this.value; // 数量
console.log(number)
var stock = goods[color]; // 该颜色手机对应的当前库存
numberInfo.innerHTML = number;
if (!color) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择手机颜色';
return;
}
if (((number - 0) | 0) !== number - 0) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
if (number > stock) {
nextBtn.disabled = true;
nextBtn.innerHTML = '库存不足';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车'
}
</script>
</body>
增加手机内存下拉选



还需要添加选择内存的事件
javascript
memorySelect.onchange = function () {
var color = colorSelect.value; // 颜色
var number = numberInput.value; // 数量
var memory = this.value; // 内存
var stock = goods[color+'|' +memory]; // 该颜色手机对应的当前库存
memoryInfo.innerHTML = memory;
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;
}
if (number > stock) {
nextBtn.disabled = true;
nextBtn.innerHTML = '库存不足';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车'
}
我们只是增加一个内存的选择条件,就要改变如此多的代码,这是因为在目前的实现种,每个节点对象都是耦合在一起的,改变或者增加任何一个节点对象,都要通知与其相关的对象,现在用中介者来重构代码
html
<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">
您选择了颜色:<div id="colorInfo"></div><br />
您选择了内存:<div id="memoryInfo"></div><br />
您输入了数量:<div id="numberInfo"></div><br />
<button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>
<script>
var goods = { // 手机库存
'red|32G': 3,
'red|16G': 0,
'blue|32G': 1,
'blue|16G': 6,
};
var mediator=(function(){
var colorSelect = document.getElementById('colorSelect');
var memorySelect = document.getElementById('memorySelect');
var numberInput = document.getElementById('numberInput');
var colorInfo = document.getElementById('colorInfo');
var memoryInfo = document.getElementById('memoryInfo');
var numberInfo = document.getElementById('numberInfo');
var nextBtn = document.getElementById('nextBtn');
return {
changed:function(obj){
var color = colorSelect.value; // 颜色
var number = numberInput.value; // 数量
var memory = memorySelect.value; // 内存
var 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;
}
if (number > stock) {
nextBtn.disabled = true;
nextBtn.innerHTML = '库存不足';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车'
}
}
})()
colorSelect.onchange = function () {
mediator.changed(this);
}
numberInput.oninput = function () {
mediator.changed(this);
}
memorySelect.onchange = function () {
mediator.changed(this);
}
</script>
</body>
可以想像,某天又要新增一些跟需求相关的节点,比如GPU型号,那么只需要改动mefiator对象即可。