观察者模式在响应式框架中的应用

339 阅读3分钟

观察者模式简介

定义

观察者模式,又称发布-订阅模式。它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。

现实例子

现今各种直播平台盛行,多数人平时会观看一些直播。假如我们在某个直播平台上有喜欢的主播,但是她的上线时间不是很固定,为了不错过她的直播就要时不时瞄一眼手机看她有没有上线,这样十分浪费时间与精力。所以我们可以使用直播平台的订阅功能去订阅某个主播,当主播上线的时候会给所有订阅她的人推送一条消息,这样我们就可以及时知道主播已经上线去打开直播而不用浪费时间。

实现思路

  1. 指定好一个发布者(直播平台)
  2. 给发布者添加一个缓存列表,用于存放订阅者或者待执行的回调函数(订阅某一主播的所有观众名单)
  3. 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函 数(遍历名单,挨个推送上线提醒)

核心代码

    //  模拟直播平台 发布中心  
    const eventCenter = {
        // 缓存列表 存放订阅者(需订阅者提供一个update方法)
        clientList: {},
        // 订阅 增加key标识来区不同的主播 
        subscribe: function (key, observer) {
            if (!this.clientList[key]) {
                this.clientList[key] = [];
            }
            // 将订阅者添加进缓存列表
            this.clientList[key].push(observer);
        },
        // 发布 支持传递不同的key 来区分不同主博,同时支持传递额外参数
        publish: function () {
            // 获取发布的key,即不同的主播
            let key = [].shift.call(arguments),
                obs = this.clientList[key];
                // 如果没有订阅者就 停止发布
            if (!obs || obs.length === 0) {
                return false;
            }
            // arguments 是发布时额外带上的参数
            for (var i = 0, ob; ob = obs[i++];) {
                ob.update.apply(ob, arguments);
            }
        }
    };

    // 模拟三个观众 
    const ob1 = {
        name: '看官1',
        update: function (arg) {
            console.log(`${this.name}收到了${arg}`);
        }
    }
    const ob2 = {
        name: '看官2',
        update: function (arg) {
            console.log(`${this.name}收到了${arg}`);
        }
    }

    const ob3 = {
        name: '看官3',
        update: function (arg) {
            console.log(`${this.name}收到了${arg}`);
        }
    }

    // 订阅
    eventCenter.subscribe('aa', ob1);
    eventCenter.subscribe('aa', ob2);
    eventCenter.subscribe('bb', ob3);

    // 发布
    eventCenter.publish('aa', '主播a上线的提醒');
    eventCenter.publish('bb', '主播b上线的提醒');
    
    // 输出结果
    看官1收到了主播a上线的提醒
    看官2收到了主播a上线的提醒
    看官3收到了主播b上线的提醒
    

以上是一个简单的观察者模式的例子,一些前端响应式框架的实现更是离不开 观察者模式的核心思想。

redux中的体现

redux 是react官方推荐的状态管理仓库。用法暂不赘述,下面是redux的极简实现

        let createStore = (reducer) => {
            let state;
            //  缓存列表 类似上个例子的 clientList 存放所有状态变化时的响应函数
            let listeners = [];
            let getState = () => state;
            //  发布 类似上个例子中的publish方法, 在修改state的同时 遍历缓存列表 执行响应函数
            let dispath = (action) => {
                //  修改state
                state = reducer(state, action);
                // 执行所有的监听函数
                listeners.forEach((l) => l())
            }
            // 订阅 类似上个例子中的subscribe方法  订阅状态变化 将 响应动作放入缓存列表
            let subscribe = (listener) => {
                listeners.push(listener);
            }
            dispath();
            return {
                getState,
                dispath,
                subscribe
            }
        }

上述即是观察者模式在redux中的应用。createStore的两个核心Api中的subscribe与dispatch分别对应了观察者模式中的订阅,发布方法。

Vue 中的体现

Vue中利用了观察者模式进行依赖收集。当监听到数据变化时来统一通知它所有依赖项进行批量更新。以下是简化提取的相关代码。

export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    // 缓存列表
    this.subs = []
  }
  //  添加订阅 类比示例一代码中的 subscribe
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  // 发布  类比示例一代码中的publish
  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

以上即是个人对观察者模式的一些理解和总结。偏差之处,欢迎指正。