从TS接口来理解观察者和发布订阅者设计模式

377 阅读1分钟

这是我参与8月更文挑战的第9天,活动详情查看:     8月更文挑战 ​

观察者模式

核心:解决一对多的消息通信问题。 特点:观察者 :拥有一个更新的方法(收到通知的回调), 被观察对象 :拥有一个添加,删除,通知观察者的方法,和一个存放观察者的容器 手写思路

  1. 首先定义被观察对象
  2. 定义观察者
interface Subject {
  observerList: Observer[];
  addObserver: (observer: Observer) => void;
  deleteObserver: (observer: Observer) => void;
  notifyObservers: () => void;
}
interface Observer {
  notify: () => void
}

注意要点

  1. 添加观察者就是把观察者加入观察者数组。
  2. 删除则通过 indexOf 找到下标,从列表移除。
  3. 通知则 forEach 调用观察者 notify方法。
  4. 需要进行各部分参数校验。
  5. 可以增加扩展方法,onece,clear 等等。

完整版本

interface Subject {
  observerList: Observer[];
  addObserver: (observer: Observer) => void;
  deleteObserver: (observer: Observer) => void;
  notifyObservers: () => void;
}
interface Observer {
  notify: () => void
}
class CreatSubject implements Subject {
  public observerList: Observer[] = []
  public addObserver(observer: Observer): void {
    this.observerList.push(observer);
  }
  public deleteObserver(observer: Observer): void {
    let currentObserverIndex: number = this.observerList.indexOf(observer)
    currentObserverIndex != -1 ? this.observerList.splice(currentObserverIndex, 1) : console.log('不存在该观察者')
  }
  public notifyObservers(): void {
    this.observerList.forEach((observer) => {
      observer.notify();
    })
  }
}
​
class CreatObserver implements Observer {
  constructor(private name: string) { }
  public notify(): void {
    //观察者收到通知后的逻辑
    console.log(`我是${this.name},我收到通知了`)
  }
}
function testObserver(): void {
  const classroom: Subject = new CreatSubject()
  const coolFish: Observer = new CreatObserver('coolFish')
  const teacherGuo: Observer = new CreatObserver('郭老师')
  classroom.addObserver(coolFish)
  classroom.addObserver(teacherGuo)
  classroom.notifyObservers()
}
testObserver()

发布订阅者模式

核心: 解决多对多的消息通信问题。 特点:拥有三个类组成 发布者:发布的事件名称和数据。 订阅者:订阅事件名和收到订阅的回调函数。 事件中心:一个添加事件的方法,一个删除事件的方法,一个触发事件的方法 手写思路

interface Publisher {
  subscriber: string;
  data: any;
}
​
interface EventChannel {
  subjects: { [key: string]: Function[] };
  on: (subscriber: string, callback: () => void) => void;
  off: (subscriber: string, callback: () => void) => void;
  emit: (subscriber: string, data: any) => void;
}
​
interface Subscriber {
  subscriber: string;
  callback: () => void;
}

注意要点:

  1. 订阅者会为事件中心的监听事件提供订阅的事件名,和该事件触发时的回调。
  2. 发布者会为事件中心的触发事件提供触发事件的事件名,和携带的参数。
  3. 事件中心有一个事件中心列表,里面存放了事件,和对应的回调函数数组。
  4. 当有事件触发时,将该事件的值,那个装满回调函数的数组遍历触发。

完整代码

interface Publisher {
  subscriber: string;
  data: any;
}
​
interface EventBus {
  subjects: { [key: string]: Function[] };
  on: (subscriber: string, callback: () => void) => void;
  off: (subscriber: string, callback: () => void) => void;
  emit: (subscriber: string, data: any) => void;
}
​
interface Subscriber {
  subscriber: string;
  callback: () => void;
}
​
​
class CreateEventBus implements EventBus {
  // 初始化订阅者对象,键为事件名,值为该事件的回调数组
  public subjects: { [key: string]: Function[] } = {};
  // 添加订阅事件
  public on(subscriber: string, callback: () => void): void {
    if (!this.subjects[subscriber]) {
      this.subjects[subscriber] = [];
    }
    this.subjects[subscriber].push(callback);
  };
  // 取消事件
  public off(subscriber: string, callback: () => void): void {
    if (callback === null) {
      this.subjects[subscriber] = [];
    } else {
      const index: number = this.subjects[subscriber].indexOf(callback);
      ~index && this.subjects[subscriber].splice(index, 1);
    }
  };
  // 触发事件
  public emit(subscriber: string, data = null): void {
    console.log(`收到发布者信息,执行订阅事件:${subscriber}`);
    this.subjects[subscriber].forEach(item => item(data));
  };
}
​
class CreatePublisher implements Publisher {
  public subscriber: string = "";
  public data: any;
  constructor(subscriber: string, data: any) {
    this.subscriber = subscriber;
    this.data = data;
  }
}
class CreateSubscriber implements Subscriber {
  public subscriber: string = "";
  constructor(subscriber: string, callback: () => void) {
    this.subscriber = subscriber;
    this.callback = callback;
  }
  public callback(): void { };
}

小结: 从TS 来理解设计模式,主要就是利用接口即是核心,只要记住接口,然后灵活的实现该接口,你就掌握了该设计模式的手写。