javascript Design Pattern Intermediary Pattern Learning Notes

  • 2021-07-21 06:58:57
  • OfStack

Let's understand such a question first. If the demand of our front-end development is given to us by the demand side, Maybe one front-end developer will deal with multiple demanders, Therefore, it will keep in touch with multiple demanders, So in the program, it means keeping references to multiple objects. The larger the program, There will be more and more objects, The relationship between them will become more and more complicated, Now, if there is an intermediary (if it is our supervisor) to meet the needs of multiple demanders, Then the demander only needs to give all the requirements to our supervisor, who will assign tasks to us according to our workload in turn. In this way, our front-end development does not need to contact multiple business parties, but only needs to contact our supervisor (that is, intermediary), which weakens the coupling between objects.

Liezi in daily life:

The intermediary model is often encountered in our daily life. For example, we go to the housing intermediary to rent a house, and the housing intermediary forms an intermediary between the renter and the landlord; The renter doesn't care who rents the house, and the landlord renter doesn't care who it rents to. Because there is an intermediary, it needs an intermediary to complete the transaction.

The role of the mediator pattern is to remove the coupling relationship between objects. After adding a mediator object, all related objects communicate through the mediator object instead of referencing each other. Therefore, when an object sends a change, it only needs to inform the mediator object. Mediators loosely couple objects and can independently change their interactions.

The list of implementing intermediaries is as follows:

I don't know if you have played the game of hero killing. At the earliest time, heroes killed 2 people (the enemy and themselves); For this game, we first use ordinary functions to achieve the following:

For example, first define a function, which has three methods, namely win (win), lose (lose), and die (enemy death). As long as one player dies, the game is over, and it is necessary to inform its opponent that it has won; The code needs to be written as follows:


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();
}
//  Initialization 2 Objects 
var h1 = new Hero(" Zhu Yuanzhang ");
var h2 = new Hero(" Liu Bowen ");
//  Set enemies for players 
h1.enemy = h2;
h2.enemy = h1;
//  Zhu Yuanzhang is dead   You will lose 
h1.die(); //  Output   Zhu Yuanzhang lose  Liu Bowen Won

Now let's add teammates to the game

For example, now let's add teammates to the game. For example, heroes kill 6 people and 1 group. In this case, there are teammates and 3 enemies; Therefore, we need to distinguish whether the enemy or teammates need the color of the team. If the color of the team is the same, it is the same team, otherwise it is the enemy;

We can first define an array players to save all players, after creating players, loop players to set teammates or enemies for each player;

var players = [];

Then let's write the function Hero; The code is as follows:


var players = []; //  Definition 1 Array of numbers   Save all players 
function Hero(name,teamColor) {
  this.friends = [];  // Save a list of teammates 
  this.enemies = [];  //  Save Enemy List 
  this.state = 'live'; //  Player status 
  this.name = name;   //  Role name 
  this.teamColor = teamColor; //  Colors of the team 
}
Hero.prototype.win = function(){
  //  Win 
  console.log("win:" + this.name);
};
Hero.prototype.lose = function(){
  //  Lost 
  console.log("lose:" + this.name);
};
Hero.prototype.die = function(){
  //  Deaths of all teammates   By default, they are all alive 
  var all_dead = true;
  this.state = 'dead'; //  Set the player status to Death 
  for(var i = 0,ilen = this.friends.length; i < ilen; i+=1) {
    //  Traversal, if there is still 1 If 20 teammates are not dead, the game is not over yet 
    if(this.friends[i].state !== 'dead') {
      all_dead = false; 
      break;
    }
  }
  if(all_dead) {
    this.lose(); //  All teammates are dead, and the game is over 
    //  Cycle   Notify all players   Game failure 
    for(var j = 0,jlen = this.friends.length; j < jlen; j+=1) {
      this.friends[j].lose();
    }
    //  Notify all enemies of game victory 
    for(var j = 0,jlen = this.enemies.length; j < jlen; j+=1) {
      this.enemies[j].win();
    }
  }
}
//  Definition 1 Factory class to create players  
var heroFactory = function(name,teamColor) {
  var newPlayer = new Hero(name,teamColor);
  for(var i = 0,ilen = players.length; i < ilen; i+=1) {
    //  If it is the same 1 Players of the team 
    if(players[i].teamColor === newPlayer.teamColor) {
      //  Add a list of teammates to each other 
      players[i].friends.push(newPlayer);
      newPlayer.friends.push(players[i]);
    }else {
      //  Add each other to the enemy list 
      players[i].enemies.push(newPlayer);
      newPlayer.enemies.push(players[i]);
    }
  }
  players.push(newPlayer);
  return newPlayer;
};
    //  Red team 
var p1 = heroFactory("aa",'red'),
  p2 = heroFactory("bb",'red'),
  p3 = heroFactory("cc",'red'),
  p4 = heroFactory("dd",'red');
    
//  Blue team 
var p5 = heroFactory("ee",'blue'),
  p6 = heroFactory("ff",'blue'),
  p7 = heroFactory("gg",'blue'),
  p8 = heroFactory("hh",'blue');
//  Let all the Red Team players die 
p1.die();
p2.die();
p3.die();
p4.die();
// lose:dd lose:aa lose:bb lose:cc
// win:ee win:ff win:gg win:hh

As above: Hero function has two parameters, which are name (player name) and teamColor (team color).

First of all, we can judge whether it is a teammate or an enemy according to the team color; There are also three methods win (win), lose (lose), and die (die); If one person dies every time, do all the teammates who should die in the cycle die? If all the teammates die, they will lose. Therefore, they need to cycle their teammates and tell each teammate that they lost. At the same time, they need to cycle their enemies and tell their enemies that they won; Therefore, every time a person dies, it is necessary to cycle once to judge whether his teammates are dead; Therefore, each player and other players are tightly coupled in 1.

Next, we can use the intermediary pattern to improve the above demo;;

First of all, we still define Hero constructor and Hero object prototype methods. In these prototype methods of Hero object, we are no longer responsible for the specific execution logic, but transfer the operation to the mediator object, which is responsible for doing specific things. We can name the mediator object playerDirector;

In playerDirector, an exposed interface ReceiveMessage is opened, which is responsible for receiving messages sent by player objects. When player objects send messages, they always send their own this as parameters to playerDirector, so that playerDirector can identify which player object the messages come from.

The code is as follows:


var players = []; // 定义1个数组 保存所有的玩家
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(){
  // 给中介者发送1个消息,移除1个玩家
  playerDirector.ReceiveMessage('removePlayer',this);
};
// 玩家换队
Hero.prototype.changeTeam = function(color) {
  // 给中介者发送1个消息,玩家换队
  playerDirector.ReceiveMessage('changeTeam',this,color);
};
// 定义1个工厂类来创建玩家 
var heroFactory = function(name,teamColor) {
  // 创建1个新的玩家对象
  var newHero = new Hero(name,teamColor);
  // 给中介者发送消息,新增玩家
  playerDirector.ReceiveMessage('addPlayer',newHero);
  return newHero;
};
var playerDirector = (function(){
  var players = {}, // 保存所有的玩家
    operations = {}; // 中介者可以执行的操作
  // 新增1个玩家操作
  operations.addPlayer = function(player) {
    // 获取玩家队友的颜色
    var teamColor = player.teamColor;
    // 如果该颜色的玩家还没有队伍的话,则新成立1个队伍
    players[teamColor] = players[teamColor] || [];
    // 添加玩家进队伍
    players[teamColor].push(player);
   };
  // 移除1个玩家
  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) {
        // 说明这是另外1组队伍
        // 获取该队伍的玩家
        var teamPlayers = players[color];
        for(var i = 0,player; player = teamPlayers[i++]; ) {
          player.win(); // 遍历通知其他玩家win了
        }
      }
    }
  }
};
var ReceiveMessage = function(){
  // arguments的第1个参数为消息名称 获取第1个参数
  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

We can see the code above; The coupling code between players has been released, And put all the logical operations in the intermediary object to process, any operation of a certain player does not need to traverse to inform other players, but only needs to send a message to the intermediary. The intermediary receives the message and processes it. After processing the message, it will feed back the processing results to other player objects. Intermediary pattern is used to decouple the code between objects; Make the program more flexible.

Intermediary mode realizes the list of purchasing goods

The following columns are the columns in the book. For example, the columns in Taobao or Tmall are not realized in this way, and it doesn't matter. We can change them. We mainly learn the idea of using the intermediary mode to realize them.

First of all, introduce the following business: In the purchase process, you can select the color of the mobile phone and enter the purchase quantity. At the same time, there are two display areas in the page, which respectively display the color and quantity just selected by the user. There is also a button that dynamically displays the operation of the next step. We need to inquire about the inventory corresponding to the mobile phone of this color. If the inventory quantity is less than the purchase quantity this time, the button is disabled and displays the copy with insufficient inventory. Otherwise, the button is highlighted and can be clicked and displayed as a shopping cart.

The HTML code is as follows:

Select color:


  <select id="colorSelect">
    <option value=""> Please select </option>
    <option value="red"> Red </option>
    <option value="blue"> Blue </option>
  </select>
  <p> Enter the quantity purchased : <input type="text" id="numberInput"/></p>
   The color you chose: <div id="colorInfo"></div>
  <p> The quantity you entered : <div id="numberInfo"></div> </p>
  <button id="nextBtn" disabled="true"> Please select the color and purchase quantity of the mobile phone </button>

First, there is an select selection box on the page, then there is an input purchase quantity input box, and there are two display areas, which are the display areas of the selected color and the input quantity, and the button operation of the next step;

Let's first define 1:

Suppose we get the inventory of all color phones from the background in advance


var goods = {
  //  Mobile phone inventory 
  "red": 6,
  "blue": 8
};

Next, we listen to the onchange event in the drop-down box of colorSelect and the oninput event in the input box of numberInput respectively, and then make corresponding processing in these two events

The general JS code is as follows:


//  Suppose we get the inventory of all color phones from the background in advance 
var goods = {
  //  Mobile phone inventory 
  "red": 6,
  "blue": 8
};
/*
 Let's listen separately below colorSelect Of the drop-down box of onchange Events and numberInput The of the input box oninput Events, 
 Then make corresponding treatment in these two events 
*/
var colorSelect = document.getElementById("colorSelect"),
  numberInput = document.getElementById("numberInput"),
  colorInfo = document.getElementById("colorInfo"),
  numberInfo = document.getElementById("numberInfo"),
  nextBtn = document.getElementById("nextBtn");
    
//  Eavesdropping change Events 
colorSelect.onchange = function(e){
  select();
};
numberInput.oninput = function(){
  select();
};
function select(){
  var color = colorSelect.value,  //  Color 
    number = numberInput.value, //  Quantity 
    stock = goods[color]; //  Current inventory corresponding to this color mobile phone 
      
  colorInfo.innerHTML = color;
  numberInfo.innerHTML = number;

  //  If the user does not select a color, disable the button 
  if(!color) {
    nextBtn.disabled = true;
    nextBtn.innerHTML = " Please select the color of your phone ";
      return;
  }
  //  Judging whether the purchase quantity input by the user is a positive integer 
  var reg = /^\d+$/g;
  if(!reg.test(number)) {
    nextBtn.disabled = true;
    nextBtn.innerHTML = " Please enter the correct purchase quantity ";
    return;
  }
  //  If the current selected quantity is greater than the current inventory quantity, it shows that the inventory is insufficient 
  if(number > stock) {
    nextBtn.disabled = true;
    nextBtn.innerHTML = " Insufficient stock ";
    return;
  }
  nextBtn.disabled = false;
  nextBtn.innerHTML = " Put it in the shopping cart ";
}

Although the above code completes the requirements on the page, But our code is all coupled in 1, Although there are not many problems at present, if there are more and more SKU attributes with the change of demand in the future, for example, when one or more drop-down boxes are added to the page, it means selecting mobile phone memory. Now we need to calculate the color, memory and purchase quantity to judge whether nextBtn shows insufficient inventory or puts it in the shopping cart; The code is as follows:

The HTML code is as follows:


 Choose a color :
  <select id="colorSelect">
    <option value=""> Please select </option>
    <option value="red"> Red </option>
    <option value="blue"> Blue </option>
  </select>
  <br/>
  <br/>
   Select memory: 
  <select id="memorySelect">
    <option value=""> Please select </option>
    <option value="32G">32G</option>
    <option value="64G">64G</option>
  </select>
  <p> Enter the quantity purchased : <input type="text" id="numberInput"/></p>
   The color you chose: <div id="colorInfo"></div>
   You chose memory: <div id="memoryInfo"></div>
  <p> The quantity you entered : <div id="numberInfo"></div> </p>
  <button id="nextBtn" disabled="true"> Please select the color and purchase quantity of the mobile phone </button>

The JS code becomes as follows:


//  Suppose we get the inventory of all color phones from the background in advance 
var goods = {
  //  Mobile phone inventory 
  "red|32G": 6,
  "red|64G": 16,
  "blue|32G": 8,
  "blue|64G": 18
};
/*
 Let's listen separately below colorSelect Of the drop-down box of onchange Events and numberInput The of the input box oninput Events, 
 Then make corresponding treatment in these two events 
 */
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");
    
//  Eavesdropping change Events 
colorSelect.onchange = function(){
  select();
};
numberInput.oninput = function(){
  select();
};
memorySelect.onchange = function(){
  select();  
};
function select(){
  var color = colorSelect.value,  //  Color 
    number = numberInput.value, //  Quantity 
    memory = memorySelect.value, //  Memory 
    stock = goods[color + '|' +memory]; //  Current inventory corresponding to this color mobile phone 
      
  colorInfo.innerHTML = color;
  numberInfo.innerHTML = number;
  memoryInfo.innerHTML = memory;
  //  If the user does not select a color, disable the button 
  if(!color) {
    nextBtn.disabled = true;
    nextBtn.innerHTML = " Please select the color of your phone ";
      return;
    }
    //  Judging whether the purchase quantity input by the user is a positive integer 
    var reg = /^\d+$/g;
    if(!reg.test(number)) {
      nextBtn.disabled = true;
      nextBtn.innerHTML = " Please enter the correct purchase quantity ";
      return;
    }
    //  If the current selected quantity is greater than the current inventory quantity, it shows that the inventory is insufficient 
    if(number > stock) {
      nextBtn.disabled = true;
      nextBtn.innerHTML = " Insufficient stock ";
      return;
    }
    nextBtn.disabled = false;
    nextBtn.innerHTML = " Put it in the shopping cart ";
  }


Related articles: