使用观察者模式,我们可以将某些对象(观察者)订阅到另一个对象(可观察对象)。当一个事件发生时,可观察对象会通知它的所有观察者!

这个模式出境的概率就比较高了,无论是在前端还是后端,都能见到它的身影,特别跟事件有关的场景。

从定义看这个模式涉及到两种对象,一种可观察对象也就是观察的目标,一种是观察者对象也叫监听者对象,观察行为的实施者。

首先实现可观察对象:

class Observable {
  constructor() {
    this.observers = [];
  }
 
  subscribe(func) {
    this.observers.push(func);
  }
 
  unsubscribe(func) {
    this.observers = this.observers.filter((observer) => observer !== func);
  }
 
  notify(data) {
    this.observers.forEach((observer) => observer(data));
  }
}

可观察对象内部维护一个数组observers用于存储观察者(其实就是一个函数),提供subcribe把观察者放入到observer数组也就是启用观察,提供unsubscribe把观察者从observer数组中移除也就是取消观察. notify方法用于通知观察者有事件触发了,也就是遍历observers数组执行每一个观察函数。

怎么用呢?现在界面上有两个按钮,我们希望点击这两个按钮都时候都会输出日志并弹出提示信息, 我们可以这样做:

6. 前端设计模式之观察者模式_设计模式

import React from "react";
import { Button, Switch, FormControlLabel } from "@material-ui/core";
import { ToastContainer, toast } from "react-toastify";
import observable from "./Observable";
//2.当事件发生时 调用notify方法,通知观察者
function handleClick() {
  observable.notify("User clicked button!");
}

function handleToggle() {
  observable.notify("User toggled switch!");
}

function logger(data) {
  console.log(`${Date.now()} ${data}`);
}

function toastify(data) {
  toast(data, {
    position: toast.POSITION.BOTTOM_RIGHT,
    closeButton: false,
    autoClose: 2000
  });
}
//1.使用subscribe注册当事件发生时执行的函数,也就是开启观察
observable.subscribe(logger);
observable.subscribe(toastify);

export default function App() {
  return (
    <div className="App">
      <Button variant="contained" onClick={handleClick}>
        Click me!
      </Button>
      <FormControlLabel
        control={<Switch name="" onChange={handleToggle} />}
        label="Toggle me!"
      />
      <ToastContainer />
    </div>
  );
}

为什么要用观察者模式呢,直接在handleClick和handlerToggler函数里触发

logger和toastify不是一样的效果?!

这样事件的触发逻辑和事件处理逻辑就紧密耦合了。对于简单的场景我们可以直接这样做,但是如果一次事件的触发需要很多不同的操作,并且有时候在我们写代码时还不知道有哪些关注这个事件的对象,这个时候用观察者模式就能很好的解耦被观察对象和观察者,被观察对象不需要知道有多少观察者,只需要简单notify就好,世界是不是突然就简单了很多?!