『面试的底气』—— 设计模式之中介者模式

1,214

作用

程序由大大小小的单一对象组成,所有这些对象都按照某种关系和规则来通信。在程序初始阶段,一个对象也行只和几个对象进行通信。但是当程序的规模增大,对象会越来越多,它们之间的通信会越来越复杂,难免会形成网状的多对多关系。而网状的多对多关系会导致对象紧密耦合在一起,这时程序就会臃肿难用维护,动一发而牵动全身。

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

用一个例子来理解

一个指挥塔对于机场是必不可少的一个存在,假如没有指挥塔,一架飞机要准备降落,飞行员要和方圆100公里的飞机通信,通过一系列计算才能确认航线和降落跑道,万一飞机员脑壳一疼计算来不及,那飞机不是降落而是坠毁了,更严重还可能跟其他飞机相撞了。用代码实现一下。

class Plane = function( name ){
  constructor(name){
    this.name = name;
    this.otherPlaneInfo = {};
  }
  calculate(){
     for(let k in this.otherPlaneInfo){
       // 计算飞机航线、降落跑道、起飞时间等等,
       // 计算来不及,或者周围的某个飞行员消息延误了,后果很严重!
     }
     this.send(msg, this.otherPlaneInfo[plane.name].plane);
  }
  send(msg,plane){
    // 向其他飞机的飞行员发送消息
    plane.receive(msg,plane);
  }
  receive(msg,plane){// 接收消息
    this.otherPlaneInfo[plane.name] = {
        msg,
        plane,
    };
    this.calculate();
  },
}

const plane1=new Plane('东方航空001号');
const plane2=new Plane('东方航空002号');
plane1.send('我要降落',plane1);
plane2.send('我要起飞',plane2);

当方圆100公里内的飞机非常多,这些飞机就构成了网状的多对多关系,有了指挥塔,每架飞机的飞行员都只需要和指挥塔通信,变成一对一的关系。指挥塔作为调停者,知道每一架飞机的飞行状况,所以它可以安排所有飞机的起降时间,及时做出航线调整。

这个场景就是应用了中介者模式,其中指挥塔Tower就是中介者。下面用代码来实现一下这个场景。

class Tower{
  constructor(){
    this.planeList = [];
  }
  addPlane(plane){ 
     this.planeList.push(plane);
  }
  calculate(msg){
     // 计算飞机航线、降落跑道、起飞时间等等
     return result
  }
  receive(msg,plane){
    let result = this.calculate(msg)
    plane.receive(result);// 计算完成后向指挥塔发送信息的飞行员发送消息。
  },
  snedAll:function(msg){// 给所有飞行员发送消息
    this.planeList.forEach(item =>{
        item.receive(msg)
    })
  }
};
const tower = new Tower();// 创建一个指挥塔
class Plane = function( name ){
  constructor(name){
    this.name = name;
  }
  send(msg){// 向指挥塔发送消息
    tower.receive(msg,this)
  }
  receive(msg){// 接收消息
    console.log(msg)
  },
}

const plane1=new Plane('东方航空001号');
const plane2=new Plane('东方航空002号');
tower.addPlane(plane1);// 将东方航空001号飞机的信息录入指挥塔
tower.addPlane(plane2);// 将东方航空002号飞机的信息录入指挥塔
plane1.send('我要降落');
plane2.send('我要起飞');

何时使用中介模式

中介者模式是迪米特原则的一种实现。迪米特原则也叫最少知识原则,是指一个对象应该尽可能少地了解另外的对象(类似不和陌生人说话)。如果对象之间的耦合性太高,一个对象发生改变之后,难免会影响到其他的对象,跟“城门失火,殃及池鱼”的道理是一样的。而在中介者模式里,对象之间几乎不知道彼此的存在,它们只能通过中介者对象来互相影响对方。

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

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

中介者模式可以非常方便地对对象进行解耦,但对象之间并非一定需要解耦。在实际项目中,对象之间有一些依赖关系是很正常的。毕竟我们写程序是为了快速完成项目交付生产,而不是过度使用设计模式。关键就在于如何去衡量对象之间的耦合程度。一般来说,如果对象之间的复杂耦合确实导致调用和维护出现了困难,那我们就可以考虑用中介者模式来重构代码。