中介者模式(Mediator),用一个中介者对象来封装一系列对象交互。中介者使各对象不需要显示地相互引用,从而使其松散,而且可以独立地改变它们之间的交互。

从生活中例子自然知道,中介者模式设计两个具体对象,一个是用户类,另一个是中介者类,根据针对接口编程原则,则需要把这两类角色进行抽象,所以中介者模 式中就有了4类角色,它们分别是:抽象中介者角色,具体中介者角色、抽象同事类和具体同事类。中介者类是起到协调各个对象的作用,则抽象中介者角色中则需 要保存各个对象的引用。有了上面的分析,则就不难理解中介者模式的结构图了,具体结构图如下所示:

为什么要使用中介者模式

  在现实生活中,中介者的存在是不可缺少的,如果没有了中介者,我们就不能与远方的朋友进行交流了。而在软件设计领域,为什么要使用中介者模式 呢?如果不使用中介者模式的话,各个同事对象将会相互进行引用,如果每个对象都与多个对象进行交互时,将会形成如下图所示的网状结构。

  从上图可以发现,如果不使用中介者模式的话,每个对象之间过度耦合,这样的既不利于类的复用也不利于扩展。如果引入了中介者模式,那么对象之间的关系将变成星型结构,采用中介者模式之后会形成如下图所示的结构:

  从上图可以发现,使用中介者模式之后,任何一个类的变化,只会影响中介者和类本身,不像之前的设计,任何一个类的变化都会引起其关联所有类的变化。这样的设计大大减少了系统的耦合度。

C#中介者模式:

namespace 中介者模式
{
class Program
{
static void Main(string[] args)
{
UnitedNationsSecurityCouncil UNSC = new UnitedNationsSecurityCouncil(); USA c1 = new USA(UNSC);
Iraq c2 = new Iraq(UNSC); UNSC.Colleague1 = c1;
UNSC.Colleague2 = c2; c1.Declare("不准研制核武器,否则要发动战争!");
c2.Declare("我们没有核武器,也不怕侵略。"); Console.Read();
}
} //联合国机构
abstract class UnitedNations
{
/// <summary>
/// 声明
/// </summary>
/// <param name="message">声明信息</param>
/// <param name="colleague">声明国家</param>
public abstract void Declare(string message, Country colleague);
} //联合国安全理事会
class UnitedNationsSecurityCouncil : UnitedNations
{
private USA colleague1;
private Iraq colleague2; public USA Colleague1
{
set { colleague1 = value; }
} public Iraq Colleague2
{
set { colleague2 = value; }
} public override void Declare(string message, Country colleague)
{
if (colleague == colleague1)
{
colleague2.GetMessage(message);
}
else
{
colleague1.GetMessage(message);
}
}
} //国家
abstract class Country
{
protected UnitedNations mediator; public Country(UnitedNations mediator)
{
this.mediator = mediator;
}
} //美国
class USA : Country
{
public USA(UnitedNations mediator)
: base(mediator)
{ }
//声明
public void Declare(string message)
{
mediator.Declare(message, this);
}
//获得消息
public void GetMessage(string message)
{
Console.WriteLine("美国获得对方信息:" + message);
}
} //伊拉克
class Iraq : Country
{
public Iraq(UnitedNations mediator)
: base(mediator)
{
} //声明
public void Declare(string message)
{
mediator.Declare(message, this);
}
//获得消息
public void GetMessage(string message)
{
Console.WriteLine("伊拉克获得对方信息:" + message);
} }
}

中介者模式的例子——泡泡堂游戏

先定义一个玩家构造函数,它有3个简单的原型方法:Play.prototype.win、Play.prototype.lose以及表示玩家死***

亡的Play.prototype.die。

因为玩家的数目是2,所以当其中一个玩家死***亡的时候游戏便结束,同时通知它的对手胜利。这段代码看起来很简单

function Player(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();
};

接下来创建2个玩家对象:

var player1 = new Player('皮蛋');
var player2 = new Player('小乖');

给玩家相互设置敌人:

player1.enemy = player2;
player2.enemy = player1;

当玩家player1被泡泡炸死的时候,只需要调用这一句代码便完成了一局游戏:

player1.die(); //输出:皮蛋 lost、小乖 won

我曾用这个游戏自娱自乐了一阵子,但不久过后觉得只有2个玩家其实没什么意思,真正的泡泡堂游戏至多可以有8个玩

家,并分成红蓝两队进行游戏。

为游戏增加队伍

现在我们改进一下游戏。因为玩家数量变多,用下面的方式来设置队友和敌人无疑很低效:
player1.partners = [player1,player2,player3,player4];
player1.enemies = [player5,player6,player7,player8];

player5.partners = [player5,player6,player7,player8];
player5.enemies = [player1,player2,player3,player4];

所以我们定义一个数组players来保存所有的玩家,在创建玩家之后,循环players来给每个玩家设置队友的敌人:

var players = [];

再改写函数player,使每个玩家对象都增加一些属性,分别是队友列表、敌人列表、玩家当前状态、角色名字以及玩家

所在的队伍颜色:

function Player(name,teamColor){
    this.partners = []; //队友列表
    this.enemies = []; //敌人列表
    this.state = 'live'; //玩家状态
    this.name = name; //角色名字
    this.teamColor = teamColor; //队伍颜色
};

玩家胜利和失败之后的展现依然很简单,只是在每个玩家的屏幕上简单地弹出提示:

Player.prototype.win = function(){ //玩家团队胜利
    console.log('winner: ' + this.name);
};

Player.prototype.lose = function(){  //玩家团队失败
    console.log('loser:' + this.name);
};

玩家死***亡的方法要变得稍微复杂一点,我们需要在每个玩家死***亡的时候,都遍历其他队友的生成状态,如果队友

全部死***亡,则这局游戏失败,同时敌人队伍所有玩家都取得胜利,代码如下:

Player.prototype.die = function(){ //玩家死***亡
    var all_dead = true;
    this.state = 'dead' //设置玩家状态为死***亡

for(var i=0,partner;partner = this.partners[i++];){ //遍历队友列表
        if(partner.state !== 'dead'){ //如果还有一个队友没有死***亡,则游戏还未失败
            all_dead = false;
            breal;
        }
    }

if(all_dead === true){  //如果队友全部死***亡
        this.lose(); //通知自己游戏失败
        for(var i=0,partner;partner = this.partners[i++];){ //通知所有队友玩家游戏失败
            partner.lose();
        }
        for(var i=0,enemy;enemy = this.enemies[i++];){  //通知所有敌人游戏胜利
            enemy.win();
        }
    }
};

最后定义一个工厂来创建玩家:
var playerFactory = function(name,teamColor){
    var newPlayer = new Player(name,teamColor); //创建新玩家

for(var 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个玩家:

//红队:
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();
player4.die();
player3.die();

玩家增多带来的困扰

现在我们已经可以随意地为游戏增加玩家或者队伍,但问题是,每个玩家和其他玩家都是紧紧耦合在一起的。在此段代

码中,每个玩家对象都有两个属性,this.partners和this.enemies,用来保存其他玩家对象的引用。当每个对象的状

态发生改变,比如角色移动、吃到道具或者死***亡时,都必须要显式地遍历通知其他对象。

在这个例子中只创建了8个玩家,或许还没有对你产生足够多的困扰,而如果在一个大型网络游戏中,画面里有成百上

千个玩家,几十支队伍在互相厮杀。如果有一个玩家掉线,必须从所有其他玩家的队友列表和敌人列表中都移除这个玩

家。游戏也许还有解除队伍和添加到别的队伍的功能,红色玩家可以突然变成蓝色玩家,这就不再仅仅是循环能够解决

的问题了。面对这样的需求,我们上面的代码可以迅速进入投降模式。

用中介者模式改造泡泡堂游戏

首先仍然是定义Player构造函数和player对象的原型方法,在player对象的这些原型方法中,不再负责具体的执行逻辑

,而是把操作转交给中介者对象,我们把中介者对象命名为playerDirector:

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 + 'lose');
};

/******************玩家死***亡******************/

Player.prototype.die = function(){
    this.state = 'dead';
    playerDirector.ReceiveMessage('playerDead',this);  //给中介者发送消息,玩家死***亡
};

/******************移除玩家******************/

Player.prototype.remove = function(){
    playerDirector.ReceiveMessage('removePlayer',this);  //给中介者发送消息,移除一个玩家
};

/*****************玩家换队*******************/

Player.prototype.changeTeam = function(color){
    playerDirector.ReceiveMessage('changeTeam',this);  //给中介者发送消息,玩家换队
};

再继续改写之前创建玩家对象的工厂函数,可以看到,因为工厂函数里不再需要给创建的玩家对象设置队友和敌人,这

个工厂函数几乎失去了工厂的意思:

var playerFactory = function(name,teamColor){
    var newPlayer = new Player(name,teamColor);   //创造一个新的玩家对象
    playDirector.ReceiveMessage('addPlayer',newPlayer);  //给中介者发送消息,新增玩家
  
    return newPlayer;
};

最后,我们需要实现这个中介者playerDirector对象,一般有以下两种方式。
1.利用发布-订阅模式。将playerDirector实现为订阅者,各player作为发布者,一旦player的状态发生改变,便推送

消息给playerDirector处理消息后将反馈发送给其他player。

2.在playerDirector中开放一些接受消息的接口,各player可以直接调用该接口来给playerDirector发送消息,player
只需要传递一个参数给playerDirector,这个参数的目的是使playerDirector可以识别发送者。同样,playerDirector
接收到消息后将处理结果反馈给其他player。

这两种方式的实现没有什么本质上的区别。在这里我们使用第二种方式,playerDirector开放一个对外暴露的接口

ReceiveMessage,负责接收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 = play.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 ReceiveMessage = function(){
        var message = Array.prototype.shift.call(arguments);  //arguments的第一个参数为消息名称
        operations[message].apply(this,arguments);
    };

return{
        ReceiveMessage:ReceiveMessage
    }
})();

可以看到,除了中介者本身,没有一个玩家知道其他任何玩家的存在,玩家与玩家之间的耦合关系完全已经解除,某个

玩家的任何操作都不需要通知其他玩家,而只需要给中介者发送一个消息,中介者处理完消息之后会把处理结果反馈给

其他的玩家对象。我们还可以继续给中介者扩展更多功能,以适应游戏需求的不断变化。

我们来看下测试结果:

//红队:

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();

假设皮蛋和小乖掉线:

player1.remove();
player2.remove();
player3.die();
player4.die();

假设皮蛋从红队叛变到蓝队:

player1.changeTeam('blue');
player2.die();
player3.die();
player4.die();

中介者模式的例子——购买商品

假设我们正在编写一个手机购买的页面,在购买流程中,可以选择手机的颜色以及输入购买数量,同时页面中有两个展

示区域,分别向用户展示刚刚选择好的颜色和数量。还有一个按钮动态显示下一步的操作,我们需要查询该颜色手机对

应的库存,如果库存数量少于这次的购买数量,按钮将被禁用并且显示库存不足,反之按钮可以点击并且显示放入购物

车。

这个需求是非常容易实现的,假设我们已经提前从后台获取到了所有颜色手机的库存量:

var goods = {  //手机库存
    "red":3,
    "blue":6
};

那么页面有可能显示为如下几种场景:

选择红色手机,购买4个,库存不足。

选择蓝色手机,购买5个,库存充足,可以加入购物车。

或者是没有输入购买数量的时候,按钮将被禁用并显示相应提示。

我们大概已经能够猜到,接下来将遇到至少5个节点,分别是:
1.下拉选择框colorSelect
2.文本输入框numberInput
3.展示颜色信息colorInfo
4.展示购买数量信息numberInfo
5.决定下一步操作的按钮nextBtn

开始编写代码

我们从编写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>
</body>

接下来将分别监听colorSelect的onchange事件函数和numberInfo的oninput事件函数,然后在这两个事件中作出相应处

理。

<script>
    var colorSelect = document.getElementById('colorSelect'),
        numberInput = document.getElementById('numberInput'),
        colorInfo = document.getElementById('colorInfo'),
        numberInfo = document.getElementById('numberInfo'),
        nextBtn = document.getElementById('nextBtn');

var goods = {   //手机库存
        "red":3,
        "blue":6
    };

colorSelect.onchange = function(){
        var color = this.value,  //颜色
            number = numberInput.value,  //数量
            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 = '放入购物车';
    };
</script>

对象之间的联系

来考虑一下,当触发了colorSelect的onchange之后,会发生什么事情。

首先我们要让colorInfo中显示当前选中的颜色,然后获取用户当前输入的购买数量,对用户的输入值进行一些合法性

判断。再根据库存数量来判断nectBtn的显示状态。

别忘了,我们还要编写numberInput的事件相关代码:

numberInput.oninput = function(){
    var color = colorSelect.value,   //颜色
        number = this.value,   //数量
        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 = '放入购物车';
    }
};

可能遇到的困难

虽然目前顺利完成了代码编写,但随之而来的需求改变有可能给我们带来麻烦。假设现在要求去掉colorInfo和

numberInfo这两个展示区域,我们就要分别改动color.Select.onchange和numberInput.onput里面的代码,因为在先前

的代码中,这些对象确实是耦合在一起的。

目前我们面临的对象还不算太多,当这个页面里的节点激增到10个或者15个时,它们之间的联系可能变得更加错综复杂

,任何一次改动都将变得棘手。为了证实这一点,我们假设页面中将新增另一个下拉选择框,代表选择手机内存。现在

我们需要计算颜色、内存和购买数量,来判断nextBtn是显示库存不足还是放入购物车。

首先我们要增加两个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>
</body>

<script>
    var colorSelect = document.getElementById('colorSelect'),
        numberInput = document.getElementById('numberInput'),
        memorySelect = document.getElementById('memorySelect'),
        colorInfo = document.getElementById('colorInfo'),
        numberInfo = document.getElementById('numberInfo'),
        memoryInfo = document.getElementById('memoryInfo'),
        nextBtn = document.getElementById('nextBtn');
</script>

接下来修改表示库存的JSON对象以及修改colorSelect的onchange事件函数:

<script>
    var goods = {   //手机库存
        "red|32G":3,  //红色32G,库存数量为3
        "red|16G":0,
        "blue|32G":1,
        "blue|16G":6
    };

colorSelect.onchange = function(){
        var color = this.value,
            memory = memorySelect.value,
            stock = goods[color + '|' + memory];

number = numberInput.value,  //数量
        colorInfo.innerHTML = color;

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 = '放入购物车';
    };
</script>

当然我们同样要改写numberInput的事件相关代码,具体代码的改变跟colorSelect大同小异。

最后还要新增memorySelect的onchange事件函数:

<script>
    memorySelect.onchange = function(){
        var color = colorSelect.value, //颜色
            number = numberInput.value, //数量
            memory = this.value,
            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 = '放入购物车';
    };
</script>

很遗憾,我们仅仅是增加一个内存的选择条件,就要改变如此多的代码,这是因为在目前的实现中,每个节点对象都是

耦合在一起的,改变或者增加任何一个节点对象,都要通知到与其相关的对象。

引入中介者

现在我们来引入中介者对象,所有的节点对象只跟中介者通信。当下拉选择框colorSelect、memorySelect和文本输入

框numberInput发生了事件行为时,它们仅仅通知中介者它们被改变了,同时把自身当作参数传入中介者,以便中介者

辨别是谁发生了改变。剩下的所有事情都交给中介者对象来完成,这样一来,无论是修改还是新增节点,都只需要改动

中介者对象里的代码。

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.onchange = 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{
        changed:function(obj){
                    //略
            var cpu = cpuSelect.value,
                stock = goods[color + '|' + memory + '|' + cpu];

if(obj === cpuSelect){
                cpuInfo.innerHTML = cpu;
            }
                    //略
        }
    }
})();

小结

中介者模式也存在一些缺点。其中,最大的缺点是系统中会新增一个中介者对象,因为对象之间交互的复杂性,转移成

了中介者对象的复杂性,使得中介者对象经常是巨大的。中介者对象自身往往就是一个难以维护的对象。

一般来说,如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长曲线,

那我们就可以考虑用中介者模式来重构代码。

中介者模式的例子——泡泡堂游戏

先定义一个玩家构造函数,它有3个简单的原型方法:Play.prototype.win、Play.prototype.lose以及表示玩家死***

亡的Play.prototype.die。

因为玩家的数目是2,所以当其中一个玩家死***亡的时候游戏便结束,同时通知它的对手胜利。这段代码看起来很简单

function Player(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();
};

接下来创建2个玩家对象:

var player1 = new Player('皮蛋');
var player2 = new Player('小乖');

给玩家相互设置敌人:

player1.enemy = player2;
player2.enemy = player1;

当玩家player1被泡泡炸死的时候,只需要调用这一句代码便完成了一局游戏:

player1.die(); //输出:皮蛋 lost、小乖 won

我曾用这个游戏自娱自乐了一阵子,但不久过后觉得只有2个玩家其实没什么意思,真正的泡泡堂游戏至多可以有8个玩

家,并分成红蓝两队进行游戏。

为游戏增加队伍

现在我们改进一下游戏。因为玩家数量变多,用下面的方式来设置队友和敌人无疑很低效:
player1.partners = [player1,player2,player3,player4];
player1.enemies = [player5,player6,player7,player8];

player5.partners = [player5,player6,player7,player8];
player5.enemies = [player1,player2,player3,player4];

所以我们定义一个数组players来保存所有的玩家,在创建玩家之后,循环players来给每个玩家设置队友的敌人:

var players = [];

再改写函数player,使每个玩家对象都增加一些属性,分别是队友列表、敌人列表、玩家当前状态、角色名字以及玩家

所在的队伍颜色:

function Player(name,teamColor){
    this.partners = []; //队友列表
    this.enemies = []; //敌人列表
    this.state = 'live'; //玩家状态
    this.name = name; //角色名字
    this.teamColor = teamColor; //队伍颜色
};

玩家胜利和失败之后的展现依然很简单,只是在每个玩家的屏幕上简单地弹出提示:

Player.prototype.win = function(){ //玩家团队胜利
    console.log('winner: ' + this.name);
};

Player.prototype.lose = function(){  //玩家团队失败
    console.log('loser:' + this.name);
};

玩家死***亡的方法要变得稍微复杂一点,我们需要在每个玩家死***亡的时候,都遍历其他队友的生成状态,如果队友

全部死***亡,则这局游戏失败,同时敌人队伍所有玩家都取得胜利,代码如下:

Player.prototype.die = function(){ //玩家死***亡
    var all_dead = true;
    this.state = 'dead' //设置玩家状态为死***亡

for(var i=0,partner;partner = this.partners[i++];){ //遍历队友列表
        if(partner.state !== 'dead'){ //如果还有一个队友没有死***亡,则游戏还未失败
            all_dead = false;
            breal;
        }
    }

if(all_dead === true){  //如果队友全部死***亡
        this.lose(); //通知自己游戏失败
        for(var i=0,partner;partner = this.partners[i++];){ //通知所有队友玩家游戏失败
            partner.lose();
        }
        for(var i=0,enemy;enemy = this.enemies[i++];){  //通知所有敌人游戏胜利
            enemy.win();
        }
    }
};

最后定义一个工厂来创建玩家:
var playerFactory = function(name,teamColor){
    var newPlayer = new Player(name,teamColor); //创建新玩家

for(var 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个玩家:

//红队:
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();
player4.die();
player3.die();

玩家增多带来的困扰

现在我们已经可以随意地为游戏增加玩家或者队伍,但问题是,每个玩家和其他玩家都是紧紧耦合在一起的。在此段代

码中,每个玩家对象都有两个属性,this.partners和this.enemies,用来保存其他玩家对象的引用。当每个对象的状

态发生改变,比如角色移动、吃到道具或者死***亡时,都必须要显式地遍历通知其他对象。

在这个例子中只创建了8个玩家,或许还没有对你产生足够多的困扰,而如果在一个大型网络游戏中,画面里有成百上

千个玩家,几十支队伍在互相厮杀。如果有一个玩家掉线,必须从所有其他玩家的队友列表和敌人列表中都移除这个玩

家。游戏也许还有解除队伍和添加到别的队伍的功能,红色玩家可以突然变成蓝色玩家,这就不再仅仅是循环能够解决

的问题了。面对这样的需求,我们上面的代码可以迅速进入投降模式。

用中介者模式改造泡泡堂游戏

首先仍然是定义Player构造函数和player对象的原型方法,在player对象的这些原型方法中,不再负责具体的执行逻辑

,而是把操作转交给中介者对象,我们把中介者对象命名为playerDirector:

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 + 'lose');
};

/******************玩家死***亡******************/

Player.prototype.die = function(){
    this.state = 'dead';
    playerDirector.ReceiveMessage('playerDead',this);  //给中介者发送消息,玩家死***亡
};

/******************移除玩家******************/

Player.prototype.remove = function(){
    playerDirector.ReceiveMessage('removePlayer',this);  //给中介者发送消息,移除一个玩家
};

/*****************玩家换队*******************/

Player.prototype.changeTeam = function(color){
    playerDirector.ReceiveMessage('changeTeam',this);  //给中介者发送消息,玩家换队
};

再继续改写之前创建玩家对象的工厂函数,可以看到,因为工厂函数里不再需要给创建的玩家对象设置队友和敌人,这

个工厂函数几乎失去了工厂的意思:

var playerFactory = function(name,teamColor){
    var newPlayer = new Player(name,teamColor);   //创造一个新的玩家对象
    playDirector.ReceiveMessage('addPlayer',newPlayer);  //给中介者发送消息,新增玩家
   
    return newPlayer;
};

最后,我们需要实现这个中介者playerDirector对象,一般有以下两种方式。
1.利用发布-订阅模式。将playerDirector实现为订阅者,各player作为发布者,一旦player的状态发生改变,便推送

消息给playerDirector处理消息后将反馈发送给其他player。

2.在playerDirector中开放一些接受消息的接口,各player可以直接调用该接口来给playerDirector发送消息,player
只需要传递一个参数给playerDirector,这个参数的目的是使playerDirector可以识别发送者。同样,playerDirector
接收到消息后将处理结果反馈给其他player。

这两种方式的实现没有什么本质上的区别。在这里我们使用第二种方式,playerDirector开放一个对外暴露的接口

ReceiveMessage,负责接收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 = play.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 ReceiveMessage = function(){
        var message = Array.prototype.shift.call(arguments);  //arguments的第一个参数为消息名称
        operations[message].apply(this,arguments);
    };

return{
        ReceiveMessage:ReceiveMessage
    }
})();

可以看到,除了中介者本身,没有一个玩家知道其他任何玩家的存在,玩家与玩家之间的耦合关系完全已经解除,某个

玩家的任何操作都不需要通知其他玩家,而只需要给中介者发送一个消息,中介者处理完消息之后会把处理结果反馈给

其他的玩家对象。我们还可以继续给中介者扩展更多功能,以适应游戏需求的不断变化。

我们来看下测试结果:

//红队:

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();

假设皮蛋和小乖掉线:

player1.remove();
player2.remove();
player3.die();
player4.die();

假设皮蛋从红队叛变到蓝队:

player1.changeTeam('blue');
player2.die();
player3.die();
player4.die();

中介者模式的例子——购买商品

假设我们正在编写一个手机购买的页面,在购买流程中,可以选择手机的颜色以及输入购买数量,同时页面中有两个展

示区域,分别向用户展示刚刚选择好的颜色和数量。还有一个按钮动态显示下一步的操作,我们需要查询该颜色手机对

应的库存,如果库存数量少于这次的购买数量,按钮将被禁用并且显示库存不足,反之按钮可以点击并且显示放入购物

车。

这个需求是非常容易实现的,假设我们已经提前从后台获取到了所有颜色手机的库存量:

var goods = {  //手机库存
    "red":3,
    "blue":6
};

那么页面有可能显示为如下几种场景:

选择红色手机,购买4个,库存不足。

选择蓝色手机,购买5个,库存充足,可以加入购物车。

或者是没有输入购买数量的时候,按钮将被禁用并显示相应提示。

我们大概已经能够猜到,接下来将遇到至少5个节点,分别是:
1.下拉选择框colorSelect
2.文本输入框numberInput
3.展示颜色信息colorInfo
4.展示购买数量信息numberInfo
5.决定下一步操作的按钮nextBtn

开始编写代码

我们从编写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>
</body>

接下来将分别监听colorSelect的onchange事件函数和numberInfo的oninput事件函数,然后在这两个事件中作出相应处

理。

<script>
    var colorSelect = document.getElementById('colorSelect'),
        numberInput = document.getElementById('numberInput'),
        colorInfo = document.getElementById('colorInfo'),
        numberInfo = document.getElementById('numberInfo'),
        nextBtn = document.getElementById('nextBtn');

var goods = {   //手机库存
        "red":3,
        "blue":6
    };

colorSelect.onchange = function(){
        var color = this.value,  //颜色
            number = numberInput.value,  //数量
            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 = '放入购物车';
    };
</script>

对象之间的联系

来考虑一下,当触发了colorSelect的onchange之后,会发生什么事情。

首先我们要让colorInfo中显示当前选中的颜色,然后获取用户当前输入的购买数量,对用户的输入值进行一些合法性

判断。再根据库存数量来判断nectBtn的显示状态。

别忘了,我们还要编写numberInput的事件相关代码:

numberInput.oninput = function(){
    var color = colorSelect.value,   //颜色
        number = this.value,   //数量
        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 = '放入购物车';
    }
};

可能遇到的困难

虽然目前顺利完成了代码编写,但随之而来的需求改变有可能给我们带来麻烦。假设现在要求去掉colorInfo和

numberInfo这两个展示区域,我们就要分别改动color.Select.onchange和numberInput.onput里面的代码,因为在先前

的代码中,这些对象确实是耦合在一起的。

目前我们面临的对象还不算太多,当这个页面里的节点激增到10个或者15个时,它们之间的联系可能变得更加错综复杂

,任何一次改动都将变得棘手。为了证实这一点,我们假设页面中将新增另一个下拉选择框,代表选择手机内存。现在

我们需要计算颜色、内存和购买数量,来判断nextBtn是显示库存不足还是放入购物车。

首先我们要增加两个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>
</body>

<script>
    var colorSelect = document.getElementById('colorSelect'),
        numberInput = document.getElementById('numberInput'),
        memorySelect = document.getElementById('memorySelect'),
        colorInfo = document.getElementById('colorInfo'),
        numberInfo = document.getElementById('numberInfo'),
        memoryInfo = document.getElementById('memoryInfo'),
        nextBtn = document.getElementById('nextBtn');
</script>

接下来修改表示库存的JSON对象以及修改colorSelect的onchange事件函数:

<script>
    var goods = {   //手机库存
        "red|32G":3,  //红色32G,库存数量为3
        "red|16G":0,
        "blue|32G":1,
        "blue|16G":6
    };

colorSelect.onchange = function(){
        var color = this.value,
            memory = memorySelect.value,
            stock = goods[color + '|' + memory];

number = numberInput.value,  //数量
        colorInfo.innerHTML = color;

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 = '放入购物车';
    };
</script>

当然我们同样要改写numberInput的事件相关代码,具体代码的改变跟colorSelect大同小异。

最后还要新增memorySelect的onchange事件函数:

<script>
    memorySelect.onchange = function(){
        var color = colorSelect.value, //颜色
            number = numberInput.value, //数量
            memory = this.value,
            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 = '放入购物车';
    };
</script>

很遗憾,我们仅仅是增加一个内存的选择条件,就要改变如此多的代码,这是因为在目前的实现中,每个节点对象都是

耦合在一起的,改变或者增加任何一个节点对象,都要通知到与其相关的对象。

引入中介者

现在我们来引入中介者对象,所有的节点对象只跟中介者通信。当下拉选择框colorSelect、memorySelect和文本输入

框numberInput发生了事件行为时,它们仅仅通知中介者它们被改变了,同时把自身当作参数传入中介者,以便中介者

辨别是谁发生了改变。剩下的所有事情都交给中介者对象来完成,这样一来,无论是修改还是新增节点,都只需要改动

中介者对象里的代码。

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.onchange = 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{
        changed:function(obj){
                    //略
            var cpu = cpuSelect.value,
                stock = goods[color + '|' + memory + '|' + cpu];

if(obj === cpuSelect){
                cpuInfo.innerHTML = cpu;
            }
                    //略
        }
    }
})();

小结

中介者模式也存在一些缺点。其中,最大的缺点是系统中会新增一个中介者对象,因为对象之间交互的复杂性,转移成

了中介者对象的复杂性,使得中介者对象经常是巨大的。中介者对象自身往往就是一个难以维护的对象。

一般来说,如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长曲线,

那我们就可以考虑用中介者模式来重构代码。

js中介者模式的更多相关文章

  1. javascript 中介者模式 mediator

    * player.js /** * 中介者模式 * @param {*} name 角色名称 * @param {*} teamColor 队伍颜色 */ function Player(name, ...

  2. JS常用的设计模式(11)—— 中介者模式

    中介者对象可以让各个对象之间不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互. 打个比方,军火买卖双方为了安全起见,找了一个信任的中介来进行交易.买家A把钱交给中介B,然后从中 ...

  3. js设计模式——8.中介者模式

    js设计模式——8.中介者模式 /*js设计模式——中介者模式*/ class A { constructor() { this.number = 0; } setNumber(num, m) { t ...

  4. 6.js模式-中介者模式

    1. 中介者模式 所有对象通过中介者进行通信 var playDirector = (function(){ var players = []; var options = {}; options.a ...

  5. 大熊君说说JS与设计模式之------中介者模式Mediator

    一,总体概要 1,笔者浅谈 我们从日常的生活中打个简单的比方,我们去房屋中介租房,房屋中介人在租房者和房东出租者之间形成一条中介.租房者并不关心他租谁的房.房东出租者也不关心他租给谁.因为有中介的存在 ...

  6. JS设计模式(11)中介者模式

    什么是中介者模式? 中介者模式:对象和对象之间借助第三方中介者进行通信. 定义:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的 ...

  7. 设计模式--中介(Mediator)模式

    时隔很长一段时,现在又重温设计模式,上个星期学习<设计模式--代理(Proxy)模式>http://www.cnblogs.com/insus/p/4128814.html. 温故而知新, ...

  8. 23种设计模式--中介者模式-Mediator Pattern

    一.中介者模式的介绍     中介者模式第一下想到的就是中介,房子中介,婚姻中介啊等等,当然笔者也希望来个婚姻中介给我介绍一个哈哈哈,,回归正题中介者模式分成中介者类和用户类,根据接口编程的方式我们再 ...

  9. MediatorPattern(中介者模式)

    /** * 中介者模式 * @author TMAC-J * 研究了这么多设计模式,觉得无非就是几点: * 1.若两个类有耦合关系,设立一个中间类,处理两个类的关系,把两个类的耦合降低 * 2.面向接 ...

随机推荐

  1. Linq 基本操作

    在linq中排序方法有: OrderBy()  --对某列升序排序 ThenBy()    --某列升序后对另一列后续升序排序 OrderByDescending() --对某列降序排序 ThenBy ...

  2. 巨蟒django之权限10,内容梳理&&权限组件应用

    1.CRM项目内容梳理: 2.权限分配 3.权限组件的应用

  3. C#反射Assembly 详细说明(转)

    1.对C#反射机制的理解2.概念理解后,必须找到方法去完成,给出管理的主要语法3.最终给出实用的例子,反射出来dll中的方法 反射是一个程序集发现及运行的过程,通过反射可以得到*.exe或*.dll等 ...

  4. LinuxCentos系统安装Mariadb过程记录

    MariaDB数据库简介 MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可. 开发这个分支的原因之一是:甲骨文公司收购了MySQL后,有将MySQL闭源的潜 ...

  5. 使用Kotlin开发Android应用 - 环境搭建 (1)

    一. 在Android Studio上安装Kotlin插件 按快捷键Command+, -> 在Preferences界面找到Plugins -> 点击Browse repositorie ...

  6. tfboys——tensorflow模块学习(三)

    tf.estimator模块 定义在:tensorflow/python/estimator/estimator_lib.py 估算器(Estimator): 用于处理模型的高级工具. 主要模块 ex ...

  7. MAXIMUM SUBSEQUENCE SUM PROBLEM

    排除不合理的项(负值), 设定一个标杆sum, 往后扫描看是否有比sum好的情况. We should ensure the following conditions: 1. The result m ...

  8. 【转】jQuery插件之ajaxFileUpload

    转自:http://www.cnblogs.com/kissdodog/archive/2012/12/15/2819025.html 说在前头,本文出自上面的作者,只是以前存的一些网址不见了,怕以后 ...

  9. PAT 天梯赛 L1-025. 正整数A+B 【字符串处理】

    题目链接 https://www.patest.cn/contests/gplt/L1-025 思路 注意 输入字符串B的时候 要用getline 因为 可能存在空格 然后就把字符串 转化成 数字 并 ...

  10. c# 内部类使用接口IComparer实现排序

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...