设计模式:(Observer Pattern )观察者模式

78 阅读3分钟

欢迎来到 TypeScript 中的设计模式系列,它介绍了使用TypeScript进行Web开发的一些有用的设计模式。

系列文章如下:

设计模式对 Web 开发人员来说非常重要,我们可以通过掌握它们来编写更好的代码。在本文中,我将使用TypeScript来介绍责任链模式

观察者模式使用非常广泛,MutationObserverIntersectionObserver, PerformanceObserver,这些API中都可以看到观察者模式。此外,此模式还用于事件监控和数据响应。

观察者模式定义了一对多的关系,允许多个观察者对象同时监控一个主体-对象。当subject-object的状态发生变化时,会通知到所有的观察者对象,以便他们可以自动更新。

观察者模式中有两个主要角色:SubjectObserver 为了更好的理解他们,我们先看下UML类图:

image.png

在上图中,我们分别定义了两个用于描述ObserverSubject对象的两个接口。

interface Observer {
    notify(article: Article): void;
}
interface Subject {
    observers: Observer[];
    addObserver(observer: Observer): void;
    deleteObserver(observer: Observer): void;
    notifyObservers(article: Article): void;
}

然后我们定义两个类分别实现以上两个接口。

ConcreteObserver 类

class ConcreteObserver implements Observer {
  constructor(private name: string) {}  
  notify(article: Article) {
    console.log(`"Article: ${article.title}" has been sent to ${this.name}.`);
  }
}

ConcreteSubject 类

class ConcreteSubject implements Subject {
  public observers: Observer[] = [];  
  public addObserver(observer: Observer): void {
    this.observers.push(observer);
  }  
  public deleteObserver(observer: Observer): void {
    const n: number = this.observers.indexOf(observer);
    n != -1 && this.observers.splice(n, 1);
  }  
  public notifyObservers(article: Article): void {
    this.observers.forEach((observer) => observer.notify(article));
  }
}

接下来验证对应函数

const subject: Subject = new ConcreteSubject();
const chris1993 = new ConcreteObserver('Chris1993');
const bytefish = new ConcreteObserver('Bytefish');
subject.addObserver(chris1993);
subject.addObserver(bytefish);
subject.notifyObservers({
  author: 'Bytefer',
  title: 'Observer Pattern in TypeScript',
  url: 'https://juejin.cn/***',
});
subject.deleteObserver(bytefish);
subject.notifyObservers({
  author: 'Bytefer',
  title: 'Adapter Pattern in TypeScript',
  url: 'https://juejin.cn/***',
});

打印结果为:

 "Article: Observer Pattern in TypeScript" has been sent to Chris1993.
 "Article: Observer Pattern in TypeScript" has been sent to Bytefish.
 "Article: Adapter Pattern in TypeScript" has been sent to Chris1993.

(Publish–subscribe Pattern)发布订阅模式

在软件架构中,发布-订阅是一种消息传递范式,其中消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。相反,发布的消息被分成不同的类别并发送给不同的订阅者。同样,订阅者可以表达对一个或多个类别的兴趣,并且只接收感兴趣的消息而不知道存在哪些发布者。

发布-订阅模型中有三个主要角色:(Publishers)发布者、(Channels)频道和(Subscribers)订阅者。

让我们基于发布-订阅模式实现一个 EventEmitter:

type EventHandler = (...args: any[]) => any;

class EventEmitter {
  private c = new Map<string, EventHandler[]>();

  subscribe(topic: string, ...handlers: EventHandler[]) {
    let topics = this.c.get(topic);
    if (!topics) {
      this.c.set(topic, (topics = []));
    }
    topics.push(...handlers);
  }

  unsubscribe(topic: string, handler?: EventHandler): boolean {
    if (!handler) {
      return this.c.delete(topic);
    }

    const topics = this.c.get(topic);
    if (!topics) {
      return false;
    }

    const index = topics.indexOf(handler);

    if (index < 0) {
      return false;
    }
    topics.splice(index, 1);
    if (topics.length === 0) {
      this.c.delete(topic);
    }
    return true;
  }

  publish(topic: string, ...args: any[]): any[] | null {
    const topics = this.c.get(topic);
    if (!topics) {
      return null;
    }
    return topics.map((handler) => {
      try {
        return handler(...args);
      } catch (e) {
        console.error(e);
        return null;
      }
    });
  }
}

对于 EventEmitter的使用

const eventEmitter = new EventEmitter();
eventEmitter.subscribe('ts', (msg) => console.log(`Received:${msg}`));
eventEmitter.publish('ts', 'Observer pattern');
eventEmitter.unsubscribe('ts');
eventEmitter.publish('ts', 'Pub-Sub pattern');

运行会在控制台打印:Received:Observer pattern

在事件驱动的架构中,发布-订阅模式起着重要的作用。该模式的具体实现可以作为事件的总线,实现同一系统中不同组件或模块之间的消息通信。对于广泛使用的插件架构,可以用来实现在不同插件之间进行消息通信。