luoxue-victor / my-blog

公众号【前端技匠】作者

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

专题9: 发布订阅者模式

luoxue-victor opened this issue · comments

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

优缺点

优点

  1. 观察者和被观察者是抽象耦合的。
  2. 建立一套触发机制。

缺点

  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

实战

比如公众号,有多个人订阅,每天定时发送公众号文章

  1. 建立一个 Persen 类,用于创建人物(观察者/订阅着)
  2. 建立 Subject 类,用于建立与观察者之间的关系(被注入到观察者的依赖)
  3. 修改状态触发更新
// 公众号订阅者
abstract class Persen {
  abstract update(): void;
  protected subject: Subject;
}
// 状态
type state = 'await' | 'publish'
// 依赖
class Subject {
  private _state: state = 'await'
  // 依赖集合
  subs: Persen[] = [];
  // 防止频繁设置状态
  lock = false
  // 设置状态,如果是发布状态的话,就发布文章
  set state(state: state) {
    // 锁上之后就不能设置状态了,只有锁解开后才可以设置状态
    if (this.lock || (this._state = state) === 'await') return;
    this.lock = true;
    Promise.resolve().then(() => {
      this.notify();
      this.lock = false;
    });
  }
  // 获得当前状态
  get state(): state {
    return this._state
  }
  // 添加订阅
  attach(persen: Persen) {
    this.subs.push(persen)
  }
  // 通知更新
  notify() {
    this.subs.forEach(sub => {
      sub.update();
    });
  }
}
// 创建一个 Tom
class Tom extends Persen {
  constructor(subject: Subject) {
    super();
    subject.attach(this)
  }
  update() {
    console.log('通知到了 Tom');
  }
}
// 创建一个 Jick
class Jick extends Persen {
  constructor(subject: Subject) {
    super();
    subject.attach(this)
  }
  update() {
    console.log('通知到了 Jick');
  }
}
// 实例化依赖
const subject = new Subject()

// Tom Jick 订阅公众号
new Tom(subject)
new Jick(subject)

// 因为设置了 lock 所以在一次 event loop 只会执行一次 
subject.state = 'publish'
subject.state = 'await'
console.log(subject.state) // publish
subject.state = 'publish'

setTimeout(() => {
  subject.state = 'publish'
}, 1000)

// 通知到了 Tom
// 通知到了 Jick
// 一秒后...
// 通知到了 Tom
// 通知到了 Jick