javascript设计模式之中介者模式学习笔记

先来理解这么一个问题,假如我们前端开发接的需求是需求方给我们需求,可能一个前端开发会和多个需求方打交道,所以会保持多个需求方的联系,那么在程序里面就意味着保持多个对象的引用,当程序的规模越大,对象会越来越多,他们之间的关系会越来越复杂,那现在假如现在有一个中介者(假如就是我们的主管)来对接多个需求方的需求,那么需求方只需要把所有的需求给我们主管就可以,主管会依次看我们的工作量来给我们分配任务,这样的话,我们前端开发就不需要和多个业务方联系,我们只需要和我们主管(也就是中介)联系即可,这样的好处就弱化了对象之间的耦合。

日常生活中的列子:

中介者模式对于我们日常生活中经常会碰到,比如我们去房屋中介去租房,房屋中介人在租房者和房东出租者之间形成一条中介;租房者并不关心租谁的房,房东出租者也并不关心它租给谁,因为有中介,所以需要中介来完成这场交易。

中介者模式的作用是解除对象与对象之间的耦合关系,增加一个中介对象后,所有的相关对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发送改变时,只需要通知中介者对象即可。中介者使各个对象之间耦合松散,而且可以独立地改变它们之间的交互。

实现中介者的列子如下:

不知道大家有没有玩过英雄杀这个游戏,最早的时候,英雄杀有2个人(分别是敌人和自己);我们针对这个游戏先使用普通的函数来实现如下:

比如先定义一个函数,该函数有三个方法,分别是win(赢), lose(输),和die(敌人死亡)这三个函数;只要一个玩家死亡该游戏就结束了,同时需要通知它的对手胜利了; 代码需要编写如下:

function Hero(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个对象
var h1 = new Hero("朱元璋");
var h2 = new Hero("刘伯温");
// 给玩家设置敌人
h1.enemy = h2;
h2.enemy = h1;
// 朱元璋死了 也就输了
h1.die(); // 输出 朱元璋lose 刘伯温Won

现在我们再来为游戏添加队友

比如现在我们来为游戏添加队友,比如英雄杀有6人一组,那么这种情况下就有队友,敌人也有3个;因此我们需要区分是敌人还是队友需要队的颜色这个字段,如果队的颜色相同的话,那么就是同一个队的,否则的话就是敌人;

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

var players = [];

接着我们再来编写Hero这个函数;代码如下:

var players = []; // 定义一个数组 保存所有的玩家
function Hero(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(){
  // 所有队友死亡情况 默认都是活着的
  var all_dead = true;
  this.state = 'dead'; // 设置玩家状态为死亡
  for(var i = 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(var j = 0,jlen = this.friends.length; j < jlen; j+=1) {
      this.friends[j].lose();
    }
    // 通知所有敌人游戏胜利
    for(var j = 0,jlen = this.enemies.length; j < jlen; j+=1) {
      this.enemies[j].win();
    }
  }
}
// 定义一个工厂类来创建玩家
var heroFactory = function(name,teamColor) {
  var newPlayer = new Hero(name,teamColor);
  for(var i = 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);
  return newPlayer;
};
    // 红队
var p1 = heroFactory("aa",'red'),
  p2 = heroFactory("bb",'red'),
  p3 = heroFactory("cc",'red'),
  p4 = heroFactory("dd",'red');

// 蓝队
var p5 = heroFactory("ee",'blue'),
  p6 = heroFactory("ff",'blue'),
  p7 = heroFactory("gg",'blue'),
  p8 = heroFactory("hh",'blue');
// 让红队玩家全部死亡
p1.die();
p2.die();
p3.die();
p4.die();
// lose:dd lose:aa lose:bb lose:cc
// win:ee win:ff win:gg win:hh

如上代码:Hero函数有2个参数,分别是name(玩家名字)和teamColor(队颜色),

首先我们可以根据队颜色来判断是队友还是敌人;同样也有三个方法win(赢),lose(输),和die(死亡);如果每次死亡一个人的时候,循环下该死亡的队友有没有全部死亡,如果全部死亡了的话,就输了,因此需要循环他们的队友,分别告诉每个队友中的成员他们输了,同时需要循环他们的敌人,分别告诉他们的敌人他们赢了;因此每次死了一个人的时候,都需要循环一次判断他的队友是否都死亡了;因此每个玩家和其他的玩家都是紧紧耦合在一起了。

下面我们可以使用中介者模式来改善上面的demo;

首先我们仍然定义Hero构造函数和Hero对象原型的方法,在Hero对象的这些原型方法中,不再负责具体的执行的逻辑,而是把操作转交给中介者对象,中介者对象来负责做具体的事情,我们可以把中介者对象命名为playerDirector;

在playerDirector开放一个对外暴露的接口ReceiveMessage,负责接收player对象发送的消息,而player对象发送消息的时候,总是把自身的this作为参数发送给playerDirector,以便playerDirector 识别消息来自于那个玩家对象。

代码如下:

var players = []; // 定义一个数组 保存所有的玩家
function Hero(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);
};
// 定义一个工厂类来创建玩家
var heroFactory = function(name,teamColor) {
  // 创建一个新的玩家对象
  var newHero = new Hero(name,teamColor);
  // 给中介者发送消息,新增玩家
  playerDirector.ReceiveMessage('addPlayer',newHero);
  return newHero;
};
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 = player.teamColor,
  // 玩家所在的队伍
  teamPlayers = players[teamColor];

  var all_dead = true;
  //遍历
  for(var i = 0,player; player = teamPlayers[i++]; ) {
    if(player.state !== 'dead') {
      all_dead = false;
      break;
    }
  }
  // 如果all_dead 为true的话 说明全部死亡
  if(all_dead) {
    for(var i = 0, player; player = teamPlayers[i++]; ) {
      // 本队所有玩家lose
      player.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(){
  // arguments的第一个参数为消息名称 获取第一个参数
  var message = Array.prototype.shift.call(arguments);
  operations[message].apply(this,arguments);
};
return {
  ReceiveMessage : ReceiveMessage
};
})();
// 红队
var p1 = heroFactory("aa",'red'),
  p2 = heroFactory("bb",'red'),
  p3 = heroFactory("cc",'red'),
    p4 = heroFactory("dd",'red');

  // 蓝队
  var p5 = heroFactory("ee",'blue'),
    p6 = heroFactory("ff",'blue'),
    p7 = heroFactory("gg",'blue'),
    p8 = heroFactory("hh",'blue');
  // 让红队玩家全部死亡
  p1.die();
  p2.die();
  p3.die();
  p4.die();
  // lose:aa lose:bb lose:cc lose:dd
  // win:ee win:ff win:gg win:hh

我们可以看到如上代码;玩家与玩家之间的耦合代码已经解除了,而把所有的逻辑操作放在中介者对象里面进去处理,某个玩家的任何操作不需要去遍历去通知其他玩家,而只是需要给中介者发送一个消息即可,中介者接受到该消息后进行处理,处理完消息之后会把处理结果反馈给其他的玩家对象。使用中介者模式解除了对象与对象之间的耦合代码; 使程序更加的灵活.

中介者模式实现购买商品的列子

下面的列子是书上的列子,比如在淘宝或者天猫的列子不是这样实现的,也没有关系,我们可以改动下即可,我们最主要来学习下使用中介者模式来实现的思路。

首先先介绍一下业务:在购买流程中,可以选择手机的颜色以及输入购买的数量,同时页面中有2个展示区域,分别显示用户刚刚选择好的颜色和数量。还有一个按钮动态显示下一步的操作,我们需要查询该颜色手机对应的库存,如果库存数量小于这次的购买数量,按钮则被禁用并且显示库存不足的文案,反之按钮高亮且可以点击并且显示假如购物车。

HTML代码如下:

选择颜色:

  <select id="colorSelect">
    <option value="">请选择</option>
    <option value="red">红色</option>
    <option value="blue">蓝色</option>
  </select>
  <p>输入购买的数量: <input type="text" id="numberInput"/></p>
  你选择了的颜色:<div id="colorInfo"></div>
  <p>你输入的数量: <div id="numberInfo"></div> </p>
  <button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>

首先页面上有一个select选择框,然后有输入的购买数量输入框,还有2个展示区域,分别是选择的颜色和输入的数量的显示的区域,还有下一步的按钮操作;

我们先定义一下:

假设我们提前从后台获取到所有颜色手机的库存量

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

接着 我们下面分别来监听colorSelect的下拉框的onchange事件和numberInput输入框的oninput的事件,然后在这两个事件中作出相应的处理

常规的JS代码如下:

// 假设我们提前从后台获取到所有颜色手机的库存量
var goods = {
  // 手机库存
  "red": 6,
  "blue": 8
};
/*
我们下面分别来监听colorSelect的下拉框的onchange事件和numberInput输入框的oninput的事件,
然后在这两个事件中作出相应的处理
*/
var colorSelect = 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();
};
function select(){
  var color = colorSelect.value,  // 颜色
    number = numberInput.value, // 数量
    stock = goods[color]; // 该颜色手机对应的当前库存

  colorInfo.innerHTML = color;
  numberInfo.innerHTML = number;

  // 如果用户没有选择颜色的话,禁用按钮
  if(!color) {
    nextBtn.disabled = true;
    nextBtn.innerHTML = "请选择手机颜色";
      return;
  }
  // 判断用户输入的购买数量是否是正整数
  var reg = /^\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代码如下:

选择颜色:
  <select id="colorSelect">
    <option value="">请选择</option>
    <option value="red">红色</option>
    <option value="blue">蓝色</option>
  </select>
  <br/>
  <br/>
  选择内存:
  <select id="memorySelect">
    <option value="">请选择</option>
    <option value="32G">32G</option>
    <option value="64G">64G</option>
  </select>
  <p>输入购买的数量: <input type="text" id="numberInput"/></p>
  你选择了的颜色:<div id="colorInfo"></div>
  你选择了内存:<div id="memoryInfo"></div>
  <p>你输入的数量: <div id="numberInfo"></div> </p>
  <button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>

JS代码变为如下:

// 假设我们提前从后台获取到所有颜色手机的库存量
var goods = {
  // 手机库存
  "red|32G": 6,
  "red|64G": 16,
  "blue|32G": 8,
  "blue|64G": 18
};
/*
我们下面分别来监听colorSelect的下拉框的onchange事件和numberInput输入框的oninput的事件,
然后在这两个事件中作出相应的处理
 */
var colorSelect = 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();
};
function select(){
  var color = 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;
    }
    // 判断用户输入的购买数量是否是正整数
    var reg = /^\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 = "放入购物车";
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • javascript设计模式之策略模式学习笔记

    1. 理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点: 1. 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句. 2. 策略模式提供了开放-封闭原则,使代码更容易理解和扩展. 3. 策略模式中的代码可以复用. 一:使用策略模式计算奖金: 下面的demo是我在书上看到的,但是没有关系,我们只是来理解下策略模式的使用而已,我们可以使用策略模式来计算奖金问题: 比如公司的年终奖是根据

  • Javascript 中介者模式实例

    问题: 在页面上,用户会有很多的操作 ,每进行一次操作我们需要去展示操作的结果,在这里我们有了一个问题,我们应如何去展示不同的结果呢?在展示的过程中我们还要考虑当页面功能不断的增加与修改,结果展示也不断的增多,我们要用一个应对这些变化最好的方法解决问题. 解决方法: 在网盘开发中就遇到了这样的一个问题,经过几番的思考,决定了用如下的方式去解决这个问题,先看图: 从图中我们可以知道,会使用到一个中介专门去接收使用者的信息,再发送到相应的展示主题,这样我们就可以解决了展示方式不段改变的问题,如何去展

  • 深入理解JavaScript系列(36):设计模式之中介者模式详解

    介绍 中介者模式(Mediator),用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 主要内容来自:http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#mediatorpatternjavascript 正文 软件开发中,中介者是一个行为设计模式,通过提供一个统一的接口让系统的不同部分进行通信.一般,如果系统有很多子模块需要直接沟通,

  • Javascript设计模式之装饰者模式详解篇

    一.前言: 装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象. 装饰者模式的特点: 1. 在不改变原对象的原本结构的情况下进行功能添加. 2. 装饰对象和原对象具有相同的接口,可以使客户以与原对象相同的方式使用装饰对象. 3. 装饰对象中包含原对象的引用,即装饰对象是真正的原对象经过包装后的对象. 二.Javascript装饰者模式详解: 描述: 装饰者模式中,可以在运行时动态添加附加功能到对

  • 轻松掌握JavaScript中介者模式

    中介者模式的作用就是解除对象与对象之间的紧耦合关系,它也称'调停者'.所有的对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发生改变时,只需要通知中介者即可. 如:机场的指挥塔,每架飞机都只需要和指挥塔通信即可,指挥塔知道每架飞机的飞行状况,可以安排所有起降时间,调整航线等 中介者模式符合迪米特法则,即最少知识原则,指一个对象应该尽可能少地了解另外的对象.如果对象之间的耦合性太高,则改变一个对象,会牵动很多对象,难于维护.当对象耦合很紧时,要修改一个对象而不影响其它的对象是很困难的.

  • javascript设计模式之中介者模式Mediator

    一,总体概要 1,笔者浅谈 我们从日常的生活中打个简单的比方,我们去房屋中介租房,房屋中介人在租房者和房东出租者之间形成一条中介.租房者并不关心他租谁的房.房东出租者也不关心他租给谁.因为有中介的存在,这场交易才变得如此方便. 在软件的开发过程中,势必会碰到这样一种情况,多个类或多个子系统相互交互,而且交互很繁琐,导致每个类都必须知道他需要交互的类,这样它们的耦合会显得异常厉害.牵一发而动全身,后果很严重,大熊很生气!~~~~(>_<)~~~~ 好了,既然问题提出来了,那有请我们这期的主角--

  • 学习JavaScript设计模式之中介者模式

    一.定义 面向对象设计鼓励将行为分布到各个对象中,把对象划分成更小的粒度,有助于增强对象的可复用性.但由于这些细粒度对象之间的联系激增,又可能反过来降低它们的可复用性. 中介者模式的作用就是解除对象与对象之间的紧耦合关系. 二.示例:购买商品 假设我们正在开发一个购买手机的页面,购买流程中,可以选择手机颜色以及输入购买数量,同时页面中可以对应展示输入内容.还有一个按钮动态显示下一步操作(该颜色库存量充足,显示下一步:否则显示库存不足). <div> <span>请选择颜色</

  • Javascript 模式实例 中介者模式

    问题: 在页面上,用户会有很多的操作 ,每进行一次操作我们需要去展示操作的结果,在这里我们有了一个问题,我们应如何去展示不同的结果呢?在展示的过程中我们还要考虑当页面功能不断的增加与修改,结果展示也不断的增多,我们要用一个应对这些变化最好的方法解决问题. 解决方法: 在115的网盘开发中就遇到了这样的一个问题,经过几番的思考,决定了用如下的方式去解决这个问题,先看图: 从图中我们可以知道,会使用到一个中介专门去接收使用者的信息,再发送到相应的展示主题,这样我们就可以解决了展示方式不段改变的问题,

  • javascript设计模式之模块模式学习笔记

    我们通过单体模式理解了是以对象字面量的方式来创建单体模式的:比如如下的对象字面量的方式代码如下: var singleMode = { name: value, method: function(){ } }; 模块模式的思路是为单体模式添加私有变量和私有方法能够减少全局变量的使用:如下就是一个模块模式的代码结构: var singleMode = (function(){ // 创建私有变量 var privateNum = 112; // 创建私有函数 function privateFun

  • javascript设计模式之单体模式学习笔记

    单体模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一变量进行访问. 单体模式的优点是: 可以用来划分命名空间,减少全局变量的数量. 使用单体模式可以使代码组织的更为一致,使代码容易阅读和维护. 可以被实例化,且实例化一次. 什么是单体模式?单体模式是一个用来划分命名空间并将一批属性和方法组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次. 但是并非所有的对象字面量都是单体,比如说模拟数组或容纳数据的话,那么它就不是单体,但是如果是组织一批相关的属性和方法在

随机推荐