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);
};
/**
 * 这里可以看出每个对象之间的耦合性很小。
 */
posted @ 2017-06-21 09:37  Stinchan  阅读(194)  评论(0编辑  收藏  举报