javascript设计模式之中介者模式学习笔记
先来理解这么一个问题,假如我们前端开发接的需求是需求方给我们需求,可能一个前端开发会和多个需求方打交道,所以会保持多个需求方的联系,那么在程序里面就意味着保持多个对象的引用,当程序的规模越大,对象会越来越多,他们之间的关系会越来越复杂,那现在假如现在有一个中介者(假如就是我们的主管)来对接多个需求方的需求,那么需求方只需要把所有的需求给我们主管就可以,主管会依次看我们的工作量来给我们分配任务,这样的话,我们前端开发就不需要和多个业务方联系,我们只需要和我们主管(也就是中介)联系即可,这样的好处就弱化了对象之间的耦合。
日常生活中的列子:
中介者模式对于我们日常生活中经常会碰到,比如我们去房屋中介去租房,房屋中介人在租房者和房东出租者之间形成一条中介;租房者并不关心租谁的房,房东出租者也并不关心它租给谁,因为有中介,所以需要中介来完成这场交易。
中介者模式的作用是解除对象与对象之间的耦合关系,增加一个中介对象后,所有的相关对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发送改变时,只需要通知中介者对象即可。中介者使各个对象之间耦合松散,而且可以独立地改变它们之间的交互。
实现中介者的列子如下:
不知道大家有没有玩过英雄杀这个游戏,最早的时候,英雄杀有2个人(分别是敌人和自己);我们针对这个游戏先使用普通的函数来实现如下:
比如先定义一个函数,该函数有三个方法,分别是win(赢),lose(输),和die(敌人死亡)这三个函数;只要一个玩家死亡该游戏就结束了,同时需要通知它的对手胜利了;代码需要编写如下:
functionHero(name){ this.name=name; this.enemy=null; } Hero.prototype.win=function(){ console.log(this.name+'Won'); } Hero.prototype.lose=function(){ console.log(this.name+'lose'); } Hero.prototype.die=function(){ this.lose(); this.enemy.win(); } //初始化2个对象 varh1=newHero("朱元璋"); varh2=newHero("刘伯温"); //给玩家设置敌人 h1.enemy=h2; h2.enemy=h1; //朱元璋死了也就输了 h1.die();//输出朱元璋lose刘伯温Won
现在我们再来为游戏添加队友
比如现在我们来为游戏添加队友,比如英雄杀有6人一组,那么这种情况下就有队友,敌人也有3个;因此我们需要区分是敌人还是队友需要队的颜色这个字段,如果队的颜色相同的话,那么就是同一个队的,否则的话就是敌人;
我们可以先定义一个数组players来保存所有的玩家,在创建玩家之后,循环players来给每个玩家设置队友或者敌人;
varplayers=[];
接着我们再来编写Hero这个函数;代码如下:
varplayers=[];//定义一个数组保存所有的玩家 functionHero(name,teamColor){ this.friends=[];//保存队友列表 this.enemies=[];//保存敌人列表 this.state='live';//玩家状态 this.name=name;//角色名字 this.teamColor=teamColor;//队伍的颜色 } Hero.prototype.win=function(){ //赢了 console.log("win:"+this.name); }; Hero.prototype.lose=function(){ //输了 console.log("lose:"+this.name); }; Hero.prototype.die=function(){ //所有队友死亡情况默认都是活着的 varall_dead=true; this.state='dead';//设置玩家状态为死亡 for(vari=0,ilen=this.friends.length;i<ilen;i+=1){ //遍历,如果还有一个队友没有死亡的话,则游戏还未结束 if(this.friends[i].state!=='dead'){ all_dead=false; break; } } if(all_dead){ this.lose();//队友全部死亡,游戏结束 //循环通知所有的玩家游戏失败 for(varj=0,jlen=this.friends.length;j<jlen;j+=1){ this.friends[j].lose(); } //通知所有敌人游戏胜利 for(varj=0,jlen=this.enemies.length;j<jlen;j+=1){ this.enemies[j].win(); } } } //定义一个工厂类来创建玩家 varheroFactory=function(name,teamColor){ varnewPlayer=newHero(name,teamColor); for(vari=0,ilen=players.length;i<ilen;i+=1){ //如果是同一队的玩家 if(players[i].teamColor===newPlayer.teamColor){ //相互添加队友列表 players[i].friends.push(newPlayer); newPlayer.friends.push(players[i]); }else{ //相互添加到敌人列表 players[i].enemies.push(newPlayer); newPlayer.enemies.push(players[i]); } } players.push(newPlayer); returnnewPlayer; }; //红队 varp1=heroFactory("aa",'red'), p2=heroFactory("bb",'red'), p3=heroFactory("cc",'red'), p4=heroFactory("dd",'red'); //蓝队 varp5=heroFactory("ee",'blue'), p6=heroFactory("ff",'blue'), p7=heroFactory("gg",'blue'), p8=heroFactory("hh",'blue'); //让红队玩家全部死亡 p1.die(); p2.die(); p3.die(); p4.die(); //lose:ddlose:aalose:bblose:cc //win:eewin:ffwin:ggwin:hh
如上代码:Hero函数有2个参数,分别是name(玩家名字)和teamColor(队颜色),
首先我们可以根据队颜色来判断是队友还是敌人;同样也有三个方法win(赢),lose(输),和die(死亡);如果每次死亡一个人的时候,循环下该死亡的队友有没有全部死亡,如果全部死亡了的话,就输了,因此需要循环他们的队友,分别告诉每个队友中的成员他们输了,同时需要循环他们的敌人,分别告诉他们的敌人他们赢了;因此每次死了一个人的时候,都需要循环一次判断他的队友是否都死亡了;因此每个玩家和其他的玩家都是紧紧耦合在一起了。
下面我们可以使用中介者模式来改善上面的demo;
首先我们仍然定义Hero构造函数和Hero对象原型的方法,在Hero对象的这些原型方法中,不再负责具体的执行的逻辑,而是把操作转交给中介者对象,中介者对象来负责做具体的事情,我们可以把中介者对象命名为playerDirector;
在playerDirector开放一个对外暴露的接口ReceiveMessage,负责接收player对象发送的消息,而player对象发送消息的时候,总是把自身的this作为参数发送给playerDirector,以便playerDirector识别消息来自于那个玩家对象。
代码如下:
varplayers=[];//定义一个数组保存所有的玩家 functionHero(name,teamColor){ this.state='live';//玩家状态 this.name=name;//角色名字 this.teamColor=teamColor;//队伍的颜色 } Hero.prototype.win=function(){ //赢了 console.log("win:"+this.name); }; Hero.prototype.lose=function(){ //输了 console.log("lose:"+this.name); }; //死亡 Hero.prototype.die=function(){ this.state='dead'; //给中介者发送消息,玩家死亡 playerDirector.ReceiveMessage('playerDead',this); } //移除玩家 Hero.prototype.remove=function(){ //给中介者发送一个消息,移除一个玩家 playerDirector.ReceiveMessage('removePlayer',this); }; //玩家换队 Hero.prototype.changeTeam=function(color){ //给中介者发送一个消息,玩家换队 playerDirector.ReceiveMessage('changeTeam',this,color); }; //定义一个工厂类来创建玩家 varheroFactory=function(name,teamColor){ //创建一个新的玩家对象 varnewHero=newHero(name,teamColor); //给中介者发送消息,新增玩家 playerDirector.ReceiveMessage('addPlayer',newHero); returnnewHero; }; varplayerDirector=(function(){ varplayers={},//保存所有的玩家 operations={};//中介者可以执行的操作 //新增一个玩家操作 operations.addPlayer=function(player){ //获取玩家队友的颜色 varteamColor=player.teamColor; //如果该颜色的玩家还没有队伍的话,则新成立一个队伍 players[teamColor]=players[teamColor]||[]; //添加玩家进队伍 players[teamColor].push(player); }; //移除一个玩家 operations.removePlayer=function(player){ //获取队伍的颜色 varteamColor=player.teamColor, //获取该队伍的所有成员 teamPlayers=players[teamColor]||[]; //遍历 for(vari=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){ varteamColor=player.teamColor, //玩家所在的队伍 teamPlayers=players[teamColor]; varall_dead=true; //遍历 for(vari=0,player;player=teamPlayers[i++];){ if(player.state!=='dead'){ all_dead=false; break; } } //如果all_dead为true的话说明全部死亡 if(all_dead){ for(vari=0,player;player=teamPlayers[i++];){ //本队所有玩家lose player.lose(); } for(varcolorinplayers){ if(color!==teamColor){ //说明这是另外一组队伍 //获取该队伍的玩家 varteamPlayers=players[color]; for(vari=0,player;player=teamPlayers[i++];){ player.win();//遍历通知其他玩家win了 } } } } }; varReceiveMessage=function(){ //arguments的第一个参数为消息名称获取第一个参数 varmessage=Array.prototype.shift.call(arguments); operations[message].apply(this,arguments); }; return{ ReceiveMessage:ReceiveMessage }; })(); //红队 varp1=heroFactory("aa",'red'), p2=heroFactory("bb",'red'), p3=heroFactory("cc",'red'), p4=heroFactory("dd",'red'); //蓝队 varp5=heroFactory("ee",'blue'), p6=heroFactory("ff",'blue'), p7=heroFactory("gg",'blue'), p8=heroFactory("hh",'blue'); //让红队玩家全部死亡 p1.die(); p2.die(); p3.die(); p4.die(); //lose:aalose:bblose:cclose:dd //win:eewin:ffwin:ggwin:hh
我们可以看到如上代码;玩家与玩家之间的耦合代码已经解除了,而把所有的逻辑操作放在中介者对象里面进去处理,某个玩家的任何操作不需要去遍历去通知其他玩家,而只是需要给中介者发送一个消息即可,中介者接受到该消息后进行处理,处理完消息之后会把处理结果反馈给其他的玩家对象。使用中介者模式解除了对象与对象之间的耦合代码;使程序更加的灵活.
中介者模式实现购买商品的列子
下面的列子是书上的列子,比如在淘宝或者天猫的列子不是这样实现的,也没有关系,我们可以改动下即可,我们最主要来学习下使用中介者模式来实现的思路。
首先先介绍一下业务:在购买流程中,可以选择手机的颜色以及输入购买的数量,同时页面中有2个展示区域,分别显示用户刚刚选择好的颜色和数量。还有一个按钮动态显示下一步的操作,我们需要查询该颜色手机对应的库存,如果库存数量小于这次的购买数量,按钮则被禁用并且显示库存不足的文案,反之按钮高亮且可以点击并且显示假如购物车。
HTML代码如下:
选择颜色:
<selectid="colorSelect"> <optionvalue="">请选择</option> <optionvalue="red">红色</option> <optionvalue="blue">蓝色</option> </select> <p>输入购买的数量:<inputtype="text"id="numberInput"/></p> 你选择了的颜色:<divid="colorInfo"></div> <p>你输入的数量:<divid="numberInfo"></div></p> <buttonid="nextBtn"disabled="true">请选择手机颜色和购买数量</button>
首先页面上有一个select选择框,然后有输入的购买数量输入框,还有2个展示区域,分别是选择的颜色和输入的数量的显示的区域,还有下一步的按钮操作;
我们先定义一下:
假设我们提前从后台获取到所有颜色手机的库存量
vargoods={ //手机库存 "red":6, "blue":8 };
接着我们下面分别来监听colorSelect的下拉框的onchange事件和numberInput输入框的oninput的事件,然后在这两个事件中作出相应的处理
常规的JS代码如下:
//假设我们提前从后台获取到所有颜色手机的库存量 vargoods={ //手机库存 "red":6, "blue":8 }; /* 我们下面分别来监听colorSelect的下拉框的onchange事件和numberInput输入框的oninput的事件, 然后在这两个事件中作出相应的处理 */ varcolorSelect=document.getElementById("colorSelect"), numberInput=document.getElementById("numberInput"), colorInfo=document.getElementById("colorInfo"), numberInfo=document.getElementById("numberInfo"), nextBtn=document.getElementById("nextBtn"); //监听change事件 colorSelect.onchange=function(e){ select(); }; numberInput.oninput=function(){ select(); }; functionselect(){ varcolor=colorSelect.value,//颜色 number=numberInput.value,//数量 stock=goods[color];//该颜色手机对应的当前库存 colorInfo.innerHTML=color; numberInfo.innerHTML=number; //如果用户没有选择颜色的话,禁用按钮 if(!color){ nextBtn.disabled=true; nextBtn.innerHTML="请选择手机颜色"; return; } //判断用户输入的购买数量是否是正整数 varreg=/^\d+$/g; if(!reg.test(number)){ nextBtn.disabled=true; nextBtn.innerHTML="请输入正确的购买数量"; return; } //如果当前选择的数量大于当前的库存的数量的话,显示库存不足 if(number>stock){ nextBtn.disabled=true; nextBtn.innerHTML="库存不足"; return; } nextBtn.disabled=false; nextBtn.innerHTML="放入购物车"; }
上面的代码虽然是完成了页面上的需求,但是我们的代码都耦合在一起了,目前虽然问题不是很多,假如随着以后需求的改变,SKU属性越来越多的话,比如页面增加一个或者多个下拉框的时候,代表选择手机内存,现在我们需要计算颜色,内存和购买数量,来判断nextBtn是显示库存不足还是放入购物车;代码如下:
HTML代码如下:
选择颜色: <selectid="colorSelect"> <optionvalue="">请选择</option> <optionvalue="red">红色</option> <optionvalue="blue">蓝色</option> </select> <br/> <br/> 选择内存: <selectid="memorySelect"> <optionvalue="">请选择</option> <optionvalue="32G">32G</option> <optionvalue="64G">64G</option> </select> <p>输入购买的数量:<inputtype="text"id="numberInput"/></p> 你选择了的颜色:<divid="colorInfo"></div> 你选择了内存:<divid="memoryInfo"></div> <p>你输入的数量:<divid="numberInfo"></div></p> <buttonid="nextBtn"disabled="true">请选择手机颜色和购买数量</button>
JS代码变为如下:
//假设我们提前从后台获取到所有颜色手机的库存量 vargoods={ //手机库存 "red|32G":6, "red|64G":16, "blue|32G":8, "blue|64G":18 }; /* 我们下面分别来监听colorSelect的下拉框的onchange事件和numberInput输入框的oninput的事件, 然后在这两个事件中作出相应的处理 */ varcolorSelect=document.getElementById("colorSelect"), memorySelect=document.getElementById("memorySelect"), numberInput=document.getElementById("numberInput"), colorInfo=document.getElementById("colorInfo"), numberInfo=document.getElementById("numberInfo"), memoryInfo=document.getElementById("memoryInfo"), nextBtn=document.getElementById("nextBtn"); //监听change事件 colorSelect.onchange=function(){ select(); }; numberInput.oninput=function(){ select(); }; memorySelect.onchange=function(){ select(); }; functionselect(){ varcolor=colorSelect.value,//颜色 number=numberInput.value,//数量 memory=memorySelect.value,//内存 stock=goods[color+'|'+memory];//该颜色手机对应的当前库存 colorInfo.innerHTML=color; numberInfo.innerHTML=number; memoryInfo.innerHTML=memory; //如果用户没有选择颜色的话,禁用按钮 if(!color){ nextBtn.disabled=true; nextBtn.innerHTML="请选择手机颜色"; return; } //判断用户输入的购买数量是否是正整数 varreg=/^\d+$/g; if(!reg.test(number)){ nextBtn.disabled=true; nextBtn.innerHTML="请输入正确的购买数量"; return; } //如果当前选择的数量大于当前的库存的数量的话,显示库存不足 if(number>stock){ nextBtn.disabled=true; nextBtn.innerHTML="库存不足"; return; } nextBtn.disabled=false; nextBtn.innerHTML="放入购物车"; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。