JavaScript设计模式_11_中介者模式
在我们生活的世界中,每个人每个物体之间都会产生一些错综复杂的联系。在应用程序里也是一样,程序由大大小小的单一对象组成,所有这些对象都按照某种关系和规则来通信。
如下图1所示:
图1
中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。
如下图2所示:
图2
/** * pre:中介者模式 * 在生活中,每个人每个物体之间都会产生错综复杂的联系。中介者的作用就是解除对象与对象之间的紧耦合。增加一个中介者之后, * 所有的对象都通过中介者通信,而不是互相引用。当一个对象发生变化时,只需要通知中介者即可。 */ // --------- 示例1 --------- /** * 两个玩家 - 对战 * 定义win、lose、die */ var Player = function(name) { this.name = name; this.enemy = null; }; Player.prototype.win = function() { console.log(this.name + ":won."); }; Player.prototype.lose = function() { console.log(this.name + ":lost."); }; Player.prototype.die = function() { this.lose(); this.enemy.win(); }; var player1 = new Player("xiaoming"); var player2 = new Player("xiaowang"); player1.enemy = player2; player2.enemy = player1; player1.die(); //---------- 示例2 ----------- /** * 多个玩家 - 对战 * partners = [] 和 enemies = [] */ var Player = function(name, teamColor) { this.partners = []; this.enemies = []; this.state = 'alive'; this.name = name; this.teamColor = teamColor; }; Player.prototype.lose = function() { console.log(this.name + ": lost."); }; Player.prototype.win = function() { console.log(this.name + ":won."); }; Player.prototype.die = function() { var all_dead = true; this.state = "dead"; for(var a of this.partners) { if("dead" !== a.state) { all_dead = false; break; } } if(all_dead) { this.lose(); for(var a of this.partners) { a.lose(); } for(var b of this.enemies) { b.win(); } } }; // -- 玩家工厂 -- var players = []; // 存放所有玩家 var PlayerFactory = function(name, teamColor) { var newPlayer = new Player(name, teamColor); for(var i = 0, a; a = players[i++];) { if(a.teamColor === newPlayer.teamColor) { a.partners.push(newPlayer); newPlayer.partners.push(a); } else { a.enemies.push(newPlayer); newPlayer.enemies.push(a); } } players.push(newPlayer); return newPlayer; }; var player1 = PlayerFactory("player1", "red"), player2 = PlayerFactory("player2", "red"), player3 = PlayerFactory("player3", "red"), player4 = PlayerFactory("player4", "red"); var player5 = PlayerFactory("player5", "blue"), player6 = PlayerFactory("player6", "blue"), player7 = PlayerFactory("player7", "blue"), player8 = PlayerFactory("player8", "blue"); player1.die(); player2.die(); player3.die(); player4.die(); //------------ 示例3 -------------- /** * 上述代码,我们将partners和enemies作为玩家的属性,每当玩家有什么动作,比如角色移动、吃到道具、玩家死亡, * 我们都要显示的遍历其他对象。现在只是两个队,如果有成千上万的玩家,几十个队伍,如果有一个人掉线,就必须从 * 所有的玩家以及敌人列表中移除。如果有解除队伍,加入别的队伍的功能,就不是循环解决的问题了。 * 下面使用中介者模式重构代码: */ var Player = function(name, teamColor) { this.name = name; this.teamColor = teamColor; this.state = "alive"; }; Player.prototype.lose = function() { console.log(this.name + ": lost."); }; Player.prototype.win = function() { console.log(this.name + ": win."); }; Player.prototype.die = function() { playerDirector.receiveMessage("playerDead", this); }; Player.prototype.remove = function() { playerDirector.receiveMessage("playerRemove", this); }; Player.prototype.changeTeam = function(color) { playerDirector.receiveMessage("changeTeam", this, color); }; var playerDirector = (function() { var players = {}, operations = {}; operations.addPlayer = function(player) { var teamColor = player.teamColor; players[teamColor] = players[teamColor] || []; players[teamColor].push(player); }; operations.playerRemove = function(player) { var teamColor = player.teamColor; var arr = players[teamColor]; if(!arr || arr.length < 1) { return; } for(var i = 0, len = arr.length; i < len; i++) { if(arr[i] === player) { arr.splice(i, 1); break; } } }; operations.changeTeam = function(player, newTeamColor) { operations.playerRemove(player); player.teamColor = newTeamColor; operations.addPlayer(player); }; operations.playerDead = function(player) { var all_dead = true; var teamColor = player.teamColor, teamPlayers = players[teamColor]; player.state = "dead"; for(var a of teamPlayers) { if("dead" !== a.state) { all_dead = false; break; } } if(all_dead) { for(var a of teamPlayers) { // 本队队友全部死亡 a.lose(); } for(var b in players) { if(teamColor !== b) { // 告知敌人游戏胜利 for(var p of players[b]) { p.win(); } } } } }; var receiveMessage = function() { var key = Array.prototype.shift.call(arguments); if(!operations[key]) { console.log("无效的命令:" + key); return; } operations[key].apply(this, arguments); }; return { receiveMessage: receiveMessage } })(); var PlayerFactory = function(name, color) { var player = new Player(name, color); playerDirector.receiveMessage("addPlayer", player); return player; }; var player1 = PlayerFactory("player1", "red"), player2 = PlayerFactory("player2", "red"), player3 = PlayerFactory("player3", "red"), player4 = PlayerFactory("player4", "red"); var player5 = PlayerFactory("player5", "blue"), player6 = PlayerFactory("player6", "blue"), player7 = PlayerFactory("player7", "blue"), player8 = PlayerFactory("player8", "blue"); player1.changeTeam("blue"); player2.die(); player3.die(); player4.die(); // ----------- 示例4 --------------- /** * 手机购买页面: * 用户选择颜色和输入数量,页面展示区展示选中的颜色和数量。 */ var goods = { "red": 3, "blue": 6 }; var colorSelect = document.getElementById("colorSelect"); var numberInput = document.getElementById("numberInput"); var colorInfo = document.getElementById("colorInfo"); var numberInfo = document.getElementById("numberInfo"); var btn = document.getElementById("cBtn"); colorSelect.onchange = function() { var color = this.value; var stock = goods[color]; var number = numberInput.value; colorInfo.innerHTML = this.selectedOptions[0].text; if(!color) { btn.disabled = true; btn.innerHTML = "请选择手机颜色"; return; } if(!number || number < 1) { btn.disabled = true; btn.innerHTML = "请输入正确的购买数量"; return; } if(number > stock) { btn.disabled = true; btn.innerHTML = "库存不足"; return; } btn.disabled = false; numberInfo.innerHTML = number; btn.innerHTML = "放入购物车"; }; /** * 写完select的事件之后,还需要实现input框的事件。。 * 试想,如果增加手机内存的选项,则需要增加的代码更多。 * 这是因为,在实现中,每个节点对象都是耦合在一起的,改变或者增加一个对象,都要通知到其他对象。 */ //============= 示例5 ---------------- /** * 增加内存选项,使用中介者改写代码 */ var goods = { "red|32": 10, "red|16": 0, "blue|32": 20, "blue|16": 1 }; var colorSelect = document.getElementById("colorSelect"); var memorySelect = document.getElementById("memorySelect"); var numberInput = document.getElementById("numberInput"); var mediator = (function() { var colorInfo = document.getElementById("colorInfo"); var memoryInfo = document.getElementById("memoryInfo"); var numberInfo = document.getElementById("numberInfo"); var btn = document.getElementById("cBtn"); var changeEvent = function(obj) { var color = colorSelect.value; var memory = memorySelect.value; var number = numberInput.value; if(obj === colorSelect) { colorInfo.innerHTML = color; } else if(obj === memorySelect) { memoryInfo.innerHTML = memory; } else if(obj === numberInput) { numberInfo.innerHTML = number; } if(!memory) { btn.disabled = true; btn.innerHTML = "请选择内存." return; } if(!number || number < 1) { btn.disabled = true; btn.innerHTML = number ? "请输入正确的购买数量" : "请输入购买数量"; return; } // 判断库存 var stock = goods[color + "|" + memory]; if(number > stock) { btn.disabled = true; btn.innerHTML = "库存不足"; return; } btn.disabled = false; btn.innerHTML = "放入购物车"; }; return { change: changeEvent } })(); colorSelect.onchange = function() { mediator.change(this); }; memorySelect.onchange = function() { mediator.change(this); }; numberInput.oninput = function() { mediator.change(this); }; /** * 这里可以看出每个对象之间的耦合性很小。 */
作者:『Stinchan』
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。