JS设计模式——观察者模式(通俗易懂)

Observer模式的概念

Observer模式是行为模式之一,它的做用是当一个对象的状态发生变化时,可以自动通知其余关联对象,自动刷新对象状态。html

Observer模式提供给关联对象一种同步通讯的手段,使某个对象与依赖它的其余对象之间保持状态同步。html5

Observer模式的角色

Subject(被观察者)面试

被观察的对象。当须要被观察的状态发生变化时,须要通知队列中全部观察者对象。Subject须要维持(添加,删除,通知)一个观察者对象的队列列表。app

ConcreteSubjectdom

被观察者的具体实现。包含一些基本的属性状态及其余操做。函数

Observer(观察者)this

接口或抽象类。当Subject的状态发生变化时,Observer对象将经过一个callback函数获得通知。spa

ConcreteObservercode

观察者的具体实现:获得通知后将完成一些具体的业务逻辑处理。server

观察者模式( 又叫发布者-订阅者模式 )应该是最经常使用的模式之一. 在不少语言里都获得大量应用. 包括咱们平时接触的dom事件. 也是js和dom之间实现的一种观察者模式.

div.onclick = function click (){
  alert ("click")
}

只要订阅了div的click事件. 当点击div的时候, function click就会被触发.

那么到底什么是观察者模式呢. 先看看生活中的观察者模式。

好莱坞有句名言. “不要给我打电话, 我会给你打电话”. 这句话就解释了一个观察者模式的前因后果。 其中“我”是发布者, “你”是订阅者。

再举个例子,我来公司面试的时候,完事以后每一个面试官都会对我说:“请留下你的联系方式, 有消息咱们会通知你”。

在这里“我”是订阅者, 面试官是发布者。因此我不用天天或者每小时都去询问面试结果, 通信的主动权掌握在了面试官手上。而我只须要提供一个联系方式。

观察者模式能够很好的实现2个模块之间的解耦。 假如我正在一个团队里开发一个html5游戏. 当游戏开始的时候,须要加载一些图片素材。

加载好这些图片以后开始才执行游戏逻辑. 假设这是一个须要多人合做的项目. 我完成了Gamer和Map模块, 而个人同事A写了一个图片加载器loadImage.

loadImage的代码以下

loadImage(imgAry, function () {
  Map.init();
  Gamer.init();
})

当图片加载好以后, 再渲染地图, 执行游戏逻辑. 嗯, 这个程序运行良好. 忽然有一天, 我想起应该给游戏加上声音功能. 我应该让图片加载器添上一行代码.

loadImage(imgAry, function () {
  Map.init();
  Gamer.init();
  Sount.init();
})

但是写这个模块的同事A去了外地旅游. 因而我打电话给他, 喂. 你的loadImage函数在哪, 我能不能改一下, 改了以后有没有反作用.

如你所想, 各类不淡定的事发生了. 若是当初咱们能这样写呢:

loadImage.listen("ready", function () {
  Map.init();
})
loadImage.listen("ready", function () {
  Gamer.init();
})
loadImage.listen("ready", function () {
  Sount.init();
})

 loadImage完成以后, 它根本不关心未来会发生什么, 由于它的工做已经完成了. 接下来它只要发布一个信号

loadImage.trigger( "ready" );

 

下面是对观察者模式的实现

/**
 * 发布订阅模式(观察者模式)
 * handles: 事件处理函数集合
 * on: 订阅事件
 * emit: 发布事件
 * off: 删除事件
**/

class PubSub {
  constructor() {
    this.handles = {};
  }

  // 订阅事件
  on(eventType, handle) {
    if (!this.handles.hasOwnProperty(eventType)) {
      this.handles[eventType] = [];
    }
    if (typeof handle == 'function') {
      this.handles[eventType].push(handle);
    } else {
      throw new Error('缺乏回调函数');
    }
    return this;
  }

  // 发布事件
  emit(eventType, ...args) {
    if (this.handles.hasOwnProperty(eventType)) {
      this.handles[eventType].forEach((item, key, arr) => {
        item.apply(null, args);
      })
    } else {
      throw new Error(`"${eventType}"事件未注册`);
    }
    return this;
  }

  // 删除事件
  off(eventType, handle) {
    if (!this.handles.hasOwnProperty(eventType)) {
      throw new Error(`"${eventType}"事件未注册`);
    } else if (typeof handle != 'function') {
      throw new Error('缺乏回调函数');
    } else {
      this.handles[eventType].forEach((item, key, arr) => {
        if (item == handle) {
          arr.splice(key, 1);
        }
      })
    }
    return this; // 实现链式操做
  }
}

// 下面作一些骚操做
let callback = function () {
  console.log('you are so nice');
}

let pubsub = new PubSub();
pubsub.on('completed', (...args) => {
  console.log(args.join(' '));
}).on('completed', callback);

pubsub.emit('completed', 'what', 'a', 'fucking day');
pubsub.off('completed', callback);
pubsub.emit('completed', 'fucking', 'again');

 

输出值:

what a fucking day

you are so nice

fucking again

相关文章
相关标签/搜索