中介者模式

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-12-15

中介者模式(Mediator):通过中介者对象封装一系列对象之间的交互,使对象之间不再相互引用,降低他们之间的耦合。

在现实世界中,类似的系统就是,飞行控制系统。一个航站塔(中介者)处理哪个飞机可以起飞,哪个可以着陆,因为所有的通信(监听的通知或者广播的通知)都是飞机和控制塔之间进行的,而不是飞机和飞机之间进行的。一个中央集权的控制中心是这个系统成功的关键,也正是中介者在软件设计领域中所扮演的角色。

从实现角度来讲,中介者模式是观察者模式中的共享被观察者对象。在这个系统中的对象之间直接的发布/订阅关系被牺牲掉了,取而代之的是维护一个通信的中心节点。

以下部分摘抄于: Here

中介者模式里,对象之间几乎不知道彼此的存在,它们只能通过中介者对象来互相影响对方。

因此,中介者模式使各个对象之间得以解耦,以中介者和对象之间的一对多关系取代了对象之间的网状多对多关系。各个对象只需关注自身功能的实现,对象之间的交互关系交给了中介者对象来实现和维护。

不过,中介者模式也存在一些缺点。其中,最大的缺点是系统中会新增一个中介者对象,因为对象之间交互的复杂性,转移成了中介者对象的复杂性,使得中介者对象经常是巨大的。中介者对象自身往往就是一个难以维护的对象。

场景:购买商品

假设我们正在编写一个手机购买的页面,在购买流程中,可以选择手机的颜色以及输入购买数量,同时页面中有两个展示区域,分别向用户展示刚刚选择好的颜色和数量。还有一个按钮动态显示下一步的操作,我们需要查询该颜色手机对应的库存,如果库存数量少于这次的购买数量,按钮将被禁用并且显示库存不足,反之按钮可以点击并且显示放入购物车。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>中介者模式 购买商品</title>
</head>

<body>
  选择颜色:
  <select id="colorSelect">
    <option value="">请选择</option>
    <option value="red">红色</option>
    <option value="blue">蓝色</option>
  </select>

  输入购买数量:
  <input type="text" id="numberInput">

  <br>
  <br>
  您选择了颜色:<div id="colorInfo"></div><br>
  您输入了数量:<div id="numberInfo"></div><br>

  <button id="nextBtn" disabled>请选择手机颜色和购买数量</button>

</body>
<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.isNaN(+number) || +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,
      number = this.value,
      stock = goods[color]
      numberInfo.innerHTML = number;

    if (!color) {
      nextBtn.disabled = true;
      nextBtn.innerHTML = '请选择手机颜色';
      return;
    }

    if (Number.isNaN(+number) || +number < 0) { //用户输入的购买数量是否为正整数
      nextBtn.disabled = true;
      nextBtn.innerHTML = '请输入正确的购买数量';
      return;
    }

    if (number > stock) { //当前选择数量大于库存量
      nextBtn.disabled = true;
      nextBtn.innerHTML = '库存不足';
      return;
    }

    nextBtn.disabled = false;
    nextBtn.innerHTML = '放入购物车';
  }
</script>
</html>

在上个示例中,对象间联系高度耦合,而且代码冗余,只是两个输入框还好,但如果有多个的话,比如增加一个内存的选择条件,就要增加关于内存选择的memorySelect.onchange事件,并在numberInput.oninput事件和colorSelect.onchange事件中增加关于内存选择的判断的条件。这是因为在目前的实现中,每个节点对象都是耦合在一起的,改变或者增加任何一个节点对象,都要通知到与其相关的对象。此时就要考虑用到中介者模式。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>中介者模式 购买商品</title>
</head>

<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">

  <br>
  <br>
  您选择了颜色:<div id="colorInfo"></div><br>
  您选择了内存:<div id="memoryInfo"></div><br>
  您输入了数量:<div id="numberInfo"></div><br>

  <button id="nextBtn" disabled>请选择手机颜色、内存和购买数量</button>
</body>
<script>
  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) {
          nextBtn.disabled = true;
          nextBtn.innerHTML = '请填写手机数量';
          return;
        }

        if (Number.isNaN(+number) || +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)
  }

  memorySelect.onchange = function () {
    mediator.changed(this)
  }

  numberInput.oninput = function () {
    mediator.changed(this)
  }
  // 以后如果想要再增加选项,如手机CPU之类的,只需在中介者对象里加上相应配置即可。
</script>
</html>