前端知识库(lizh)
  • README
  • Bugs
    • 前端调试随笔
    • 浏览器常见问题概览
    • 浏览器兼容问题概览
    • HTML常见问题概览
    • CSS常见问题概览
    • JS常见问题概览
    • 移动端兼容性问题概览
    • 微信小程序开发
    • NodeJs常见问题概览
    • Mac常见问题概览
    • 微信开发遇坑指南
    • Npm包常见问题概览
    • 其他问题汇总
  • Css探索系列
    • CSS基础知识
    • CSS常见问答
    • CSS常见问答02
    • CSS应用示例
    • CSS应用示例02
    • 由Z Index引发的层叠上下文思考
    • 由浮动塌陷引发的块级格式上下文思考
    • CSS探索系列 Flex布局
    • CSS探索系列 Margin
    • CSS探索系列 Auto关键字
    • CSS探索系列 Gradient
    • CSS探索系列 Line Height
    • CSS探索系列 元素居中
    • CSS探索系列 动画
    • 为什么使用PostCSS处理CSS?
    • 重新认识伪类与伪元素
    • 自定义表单伪元素样式
    • 如何理解Css中的Display属性
    • 视口和软键盘对视口的影响
    • 关于Css
  • Frontend
    • 00 关于Web前端
    • 01 前端知识概览
    • 02 常用前端库概览
    • 基础 00 前端常见问题01
    • 基础 01 浏览器缓存
    • 基础 02 浏览器工作原理
    • 基础 03 谈谈前端跨源问题及解决方法
    • 进阶 01 Web性能优化
    • 进阶 02 搜索引擎优化(SEO)
    • 进阶 03 前端模块化编程
    • 进阶 04 规范代码:Linter、Prettier、EditorConfig
    • 进阶 11 前端自动化测试
    • 高级 01 前端安全
    • Vue2.X原理篇
    • Vue3初步了解及迁移指南
    • 重读Vue教程
    • React17.X原理篇
    • 你必须知道的React问题
    • 重读React教程
    • 聊一聊Cookie的一些问题
    • 如何理解HTTP响应的状态码
    • HTTP的历史演变及概述
    • Webpack4.X原理篇
    • Webpack基础入门篇
    • Webpack构建优化篇
    • TypeScript使用指南
    • 代码规范
      • 前端规范
      • HTML
      • CSS
      • JS
  • Html探索系列
    • HTML基础知识
    • HTML基础知识02
    • HTML常见问答
    • HTML经典实践用例
    • HTML元素的宽高及位置详解
    • Video元素的使用和常见问题总结
    • Html探索系列 Meta标签
    • DOCTYPE:文档类型与浏览器模式
    • DHTML(动态网页)简介
    • HTML标签详解
    • HTML布局的几种方式
    • HTML全局属性
    • 关于Html
  • Js探索系列
    • 基础知识
    • 常见问答
    • 应用示例
    • 趣味示例
    • 基础篇 05 AJAX
    • 基础篇 06 Window对象
    • 基础篇 07 Error、JSON、Math、Console对象
    • 基础篇 08 History、URL、Screen、Navigator、Location对象
    • 基础篇 09 文档对象模型(DOM)
    • 基础篇 10 Document对象
    • 基础篇 11 Element对象
    • 基础篇 12 Event对象
    • 基础篇 13 键盘、鼠标、触摸事件
    • 基础篇 15 CSS对象模型(CSSOM)
    • 进阶篇 01 Prototype对象和继承
    • 进阶篇 02 Promise对象
    • 进阶篇 07 迭代器(Iterator)
    • 进阶篇 08 Generator和Async函数
    • 进阶篇 09 JavaScript异步编程
    • Date对象和日期时间字符串格式
    • Canvas基础入门篇
    • Canvas进阶篇
    • SVG基础入门篇
    • 四种判断数据类型方法的优缺点
    • 深入理解JavaScript的浅拷贝和深拷贝
    • 谈谈JavaScript的作用域和上下文
    • 复制内容到剪贴板
    • 关于Javascript
  • NodeJs
    • 关于Node.Js
    • Node.Js:三种调试方法
    • Npm包管理器简介及一些机制
    • NPM:Package.Json详解(中文)
    • NPM:从零开始,开发一个软件包
    • NPM:常用命令
    • Node.Js:Fs(文件系统)
    • Node.Js:Global(全局变量)
    • Node.Js:HTTP
    • Node.Js:Module(模块)
    • Node.Js:Path(路径)
    • Node.Js:Readline(逐行读取)
  • Research
    • 极细边框(1px边框)实现方式
    • 如何监控前端异常?
    • H5页面跳转和刷新
    • Web主题切换和个性化定制方法总结
    • Vue SSR(服务端渲染)的简单实现
    • 基于Create React App打造代码规范化的React+Ts项目
    • H5可视化编辑
    • Web常用功能
    • Javascript加密混淆
    • Vue如何导入TypeScript
    • 移动端PDF预览
    • 纯CSS绘制箭头
    • 网站性能测量和优化方法
  • Tech
    • GOOGLE浏览器的搜索技巧
    • Curl的用法指南
    • Sublime3插件篇
    • Charles安装及使用
    • Nginx基础使用
    • 排序算法(Javascript)
    • 代码整洁之道(摘录笔记)
    • Java的24种设计模式与7大原则
    • 观察者和发布订阅模式
  • Tools
    • Git
      • Git基础教程
      • Git常见问题
    • Gitbook
      • Gitbook入门篇
      • Gitbook插件篇
      • Gitbook进阶篇
由 GitBook 提供支持
在本页
  • 观察者模式
  • 发布订阅模式
  • 观察者 vs 发布订阅
  • 参考资料

这有帮助吗?

  1. Tech

观察者和发布订阅模式

上一页Java的24种设计模式与7大原则下一页Git

最后更新于1年前

这有帮助吗?

观察者模式是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

其实 24 种基本的设计模式中并没有发布订阅模式,发布订阅模式只是观察者模式的一个别称。但是经过时间的沉淀,似乎观察者模式已经强大了起来,已经独立于观察者模式,成为另外一种不同的设计模式。

img

观察者模式

观察者模式一般至少有一个可被观察的对象 Subject ,可以有多个观察者去观察这个对象。当被观察对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

二者的关系是通过被观察者主动建立的,被观察者至少要有三个方法——添加观察者、移除观察者、通知观察者。当被观察者将某个观察者添加到自己的观察者列表后,观察者与被观察者的关联就建立起来了。此后只要被观察者在某种时机触发通知观察者方法时,观察者即可接收到来自被观察者的消息。

优点:响应式。目标变化就会通知观察者,这是观察者最大的有点,也是因为这个优点,观察者模式在前端才会这么出名。

缺点:不灵活。相比订阅发布模式,由于目标和观察者是耦合在一起的,所以观察者模式需要同时引入目标和观察者才能达到响应式的效果;而订阅发布模式只需要引入事件中心,订阅者和发布者可以不再一处。

// 被观察者
class Subject {
  constructor() {
    this.obs = [];
  }
  add(ob) {
    this.obs.push(ob);
  }
  remove(ob) {
    this.obs = this.obs.filter(o => o.name !== ob.name);
  }
  notify(message) {
    this.obs.forEach(ob => ob.notified(message));
  }
}

// 观察者
class Observer {
  constructor(name) {
    this.name = name;
  }
  notified(message) {
    console.log(`Hellow, ${this.name}. This is ${message}!`);
  }
}

const subject = new Subject();
const observerLi = new Observer('Li');
const observerZhao = new Observer('Zhao');
subject.add(observerLi);
subject.add(observerZhao);
subject.notify('Subject messagge 01');
subject.remove(observerLi);
subject.notify('Subject messagge 02');

发布订阅模式

与观察者模式相比,发布订阅核心基于一个消息中心来建立整个体系。其中发布者和订阅者不直接进行通信,而是发布者将要发布的消息交由中心管理,订阅者也是根据自己的情况,按需订阅中心中的消息。

订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。

优点:灵活。由于订阅发布模式的发布者和订阅者是解耦的,只要引入订阅发布模式的事件中心,无论在何处都可以发布订阅。同时订阅发布者相互之间不影响。

缺点:容易导致代码不好维护,使用不当就会造成数据流混乱。性能消耗更大,订阅发布模式需要维护事件列队,订阅的事件越多,内存消耗越大。

// 消息中心
class PubSub {
  constructor() {
    this.pubs = {};
    this.subs = {};
  }
  publish(type, content) {
    this.pubs[type] = this.pubs[type] || [];
    this.pubs[type].push(content);
  }
  subscribe(type, cb) {
    this.subs[type] = this.subs[type] || [];
    this.subs[type].push(cb);
  }
  notify(type) {
    const pubs = this.pubs[type] || [];
    const subs = this.subs[type] || [];
    subs.forEach((cb) => {
      pubs.forEach(msg => cb(msg))
    });
  }
}

// 发布者
class Publisher {
  constructor(name, context) {
    this.name = name;
    this.context = context;
  }
  publish(content) {
    this.context.publish(this.name, content);
  }
}

// 订阅者
class Subscriber {
  constructor(context) {
    this.context = context;
  }
  subscribe(type, cb) {
    this.context.subscribe(type, cb);
  }
}

const Type1 = 'music';
const Type2 = 'moive';
const pubsub = new PubSub();
const pubA = new Publisher(Type1, pubsub);
const pubB = new Publisher(Type2, pubsub);
const subA = new Subscriber(pubsub);
const subB = new Subscriber(pubsub);
subA.subscribe(Type1, msg => console.log(`I am A. ${msg} is published!`));
subB.subscribe(Type2, msg => console.log(`I am B. ${msg} is published!`));
pubA.publish('music01!');
pubA.publish('music02!');
pubsub.notify(Type1);
pubB.publish('moive01!');
pubsub.notify(Type2);

观察者 vs 发布订阅

从概念上理解,两者没什么不同,都是通过事件的方式在某个时间点进行触发,让观察者/订阅者可以进行相应的操作。

两者主要是在实现上有所不同:

  • 调度:观察者模式是由被观察者调度的;而发布订阅模式是统一由消息中心调度。

  • 角色数量:观察者模式只需要 2 个角色:观察者和被观察者,其中被观察者是重点;而发布订阅至少需要 3 个角色:发布者、订阅者和发布订阅中心,其中发布订阅中心是重点。

  • 耦合度:观察者模式中目标和观察者是直接关联的,耦合在一起(有些观念说观察者是解耦,解耦的是业务代码,不是目标和观察者本身);而发布订阅模式是一个事件中心调度模式,订阅者和发布者是没有直接关联的,通过事件中心进行关联,两者是解耦的。

  • 使用场景:观察者模式多用于单个目标内部;而发布订阅模式更多的是一种跨目标的模式,比如消息管理。

参考资料

稀土掘金 - 理解【观察者模式】和【发布订阅】的区别