设计模式:单例模式、发布订阅模式

519 阅读3分钟

设计模式,是在软件设计开发过程中,针对特定问题或场景较优解决方案。它可以帮助我们遇到相似的问题、场景时,能够快速找到更优的方式解决。

1. 设计模式需遵循的设计原则

  • 单一职责原则:一个类只负责一个功能,如果功能过于复杂,最好让这个类只负责一部分逻辑。
  • 开放封闭原则:对于类、函数、模块等软件实体是可扩展但不可修改的。
  • 面向接口原则:调用者只需要关注接口,不必清楚接口内部的类如何实现的。

2. 发布-订阅模式:常考

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到状态改变的通知。

订阅者把自己想订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者注册到调度中心的处理代码。

1. 和观察者模式的区别

在观察者模式中,观察者会监听被观察者的变化,当被观察者变化时就会通知所有的观察者

所以观察者模式中仅有发布者、订阅者两个,而发布-订阅模式则还有调度中心,负责处理发布者和订阅者的代码。

2. 手写实现:EventEmitter,支持事件的on,once,off,emit。

  • on:注册事件的回调函数
  • once:注册事件的回调函数,只执行一次
  • off:删除一个回调函数
  • emit:触发事件,执行回调函数
  • offAll:删除事件的所有回调函数
class EventEmitter {
  constructor() {
    this.listener = {};  // 存储事件以及回调函数
  }
  
  on(event, callback) {
    if (!this.listener[event]) {
      this.listener[event] = [];  // 初始化一个监听函数队列
    }
    this.listener[event].push(callback);
  }

  emit(event, ...args) {
    if (this.listener[event]) {
      this.listener[event].forEach((cb) => {
        cb(...args);
      })
    }
  }

  off(event, callback) {
    if (this.listener[event].includes(callback)) {
      let index = this.listener[event].indexOf(callback);
      this.listener[event].splice(index, 1);
    }
    if (this.listener[event].length == 0) { // 当没有回调函数时就移除该事件
      delete this.listener[event];  
    }
  }

  once(event, callback) {
    const wrapper = function(...args) { // 对回调函数进行包装,使其执行完毕自动被移除
      callback.apply(this, args);
      this.off(event, callback);
    }
    this.on(event, wrapper);
  }
  
  offAll(event) { // 删除某个事件的所有监听函数
      if (this.listeners[event]) {
          delete this.listeners[event];
      }
  }
}
/* 测试 */
let ee = new EventEmitter();
ee.on('eat', () => console.log('吃饭'));
ee.emit('eat');

3. 单例模式:常考

一个类只能构造出唯一的实例。

function Single(name) {  
  this.name = name;
}
Single.getInstance = function(name) { // 静态方法
  if (this.instance) {
    return this.instance;
  } else {
    this.instance = new Single(name);
    return this.instance;
  }
};

// 测试
let a = Single.getInstance('ins1');
let b = Single.getInstance('ins2');
console.log(a === b);  // true

4. 其他模式

  • 代理模式:在访问者和目标对象之间加一层代理,无法直接访问时可以通过代理实现授权和控制
  • 装饰器模式:在原来对象的基础上扩展功能或属性。
  • 适配器模式:将一个接口转换成用户希望的另一个接口,使接口不兼容的类也可以一起工作