设计模式之-中介者模式

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

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对象即可。

相关推荐
明洞日记2 小时前
【设计模式手册023】外观模式 - 如何简化复杂系统
java·设计模式·外观模式
游戏23人生3 小时前
c++ 语言教程——16面向对象设计模式(五)
开发语言·c++·设计模式
watersink4 小时前
Agent 设计模式
开发语言·javascript·设计模式
老朱佩琪!5 小时前
Unity策略模式
unity·设计模式·策略模式
o0向阳而生0o5 小时前
116、23种设计模式之责任链模式(23/23)(完结撒花)
设计模式·责任链模式
山沐与山1 天前
【设计模式】Python模板方法模式:从入门到实战
python·设计模式·模板方法模式
阿拉斯攀登1 天前
设计模式:责任链模式
设计模式·责任链模式
崎岖Qiu1 天前
【设计模式笔记18】:并发安全与双重检查锁定的单例模式
java·笔记·单例模式·设计模式
阿闽ooo1 天前
单例模式深度解析:从饿汉到懒汉的实战演进
开发语言·c++·笔记·设计模式