小站
小站
文章目录
  1. 为什么
  2. 直观对比
  3. 观察者模式
  4. 发布/订阅模式
  5. 区别
  6. 场景
  7. 写在最后

说说观察者模式和发布/订阅模式的区别

文章来源:itsCoder 的 WeeklyBolg 项目

itsCoder主页:http://itscoder.com/

作者:阿风

审阅者:小山hymanme

为什么

为什么要写这篇文章?

网上有相关的文章吗?当然有

那你为什么还要写?我就是要写

女票最近在学习设计模式,学到了观察者模式和发布/订阅模式,她看完了书以后对我说这有什么区别?于是我跟她说,这当然有区别,抄起笔就给她讲有什么区别。

那讲懂了吗?当然没有

为什么呢?她在乎的是你的态度(参考女票车发动不了的梗)

所以我要写这篇文章。别问为什么,问就右上角。

直观对比

废话不多说,先上图,看图是帮助理解最直观的方式。

对比图

这个图,应该说很直观的告诉了你,这两种设计模式有什么区别。

那有什么区别呢?右…右边多了个方框?对啊,就是多了个方框。

关键的问题是,为什么要比观察者模式多一个方框?

观察者模式

既然发布/订阅是比观察者模式多了一些东西,那么我们就先来看观察者模式。

你可以去Google,可以在很多不同的文章看到这么一句话:定义对象间一种一对多的依赖关系

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

简单来说就是,A 对象依赖 B 对象的某个状态变化来执行一些逻辑,那么 A 就需要观察 B 的某个状态,B 会在这个状态发生改变时通知 A,并且可以把描述这个状态的数据给A。

能看明白不?我举个栗子

大家都上过学吧,假设你是个学渣,你同桌是个学霸,今天要考试了,卷子发下来,你几乎一道题都不会做,于是你对同桌说:你做完了给我抄一下,你同桌做完了,拿胳膊肘捅你一下,把试卷往你这边挪。

你是观察者,你同桌是被观察者,你观察的状态是你同桌做完了,你同桌的试卷是你要的数据,这个状态发生的时候你要处理的逻辑是抄答案。

抄过作业的都知道,你不可能一直盯着你同桌或者一直问他写完没吧?在程序中,如果对象 A 一直不停的去获取 B 的某个状态来看有没有发生改变,这不是观察者模式,这是轮询。

那什么是一对多的依赖关系?

你同桌是学霸,你同桌前后左右都跟你一样是学渣,你们都得抄他的,不然全不及格。明白了吗?

维基百科那段话,最后一句— 此种模式通常被用来实时事件处理系统。

我们回到程序中来,做为前端/客户端工程师最为常见的一种场景:一个按钮被点击了,程序打开一个页面。所谓的监听这个按钮的点击事件不就是一种观察者模式吗?

发布/订阅模式

我们还是先看维基百科

软件架构中,发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。

发布/订阅是消息队列范式的兄弟,通常是更大的面向消息中间件系统的一部分。 —— 维基百科

能看明白不?网上有很多类似下面的例子,在我看来并不是发布/订阅模式

你在淘宝上看上了一款键盘,但是没货了,于是你给店家说到货了告诉我,店家说好的,到货了告诉你。

我再举个栗子

你看上了一款键盘,你找了个代购跟他说找到这个键盘就买给我,这个代购路子野有很多渠道,键盘到货后这些渠道商会告诉这个代购键盘到货了,代购买下来发给你

前者和后者有什么区别?这两个例子中消息都是键盘,而第一个消息的发布者是店家,你是消息的接收方,你明确的知道这个键盘是这个店家的,店家也明确知道你买了这个键盘,键盘出了问题你能找这个店家售后。

而后者消息的发布者是那个代购的某一个渠道商,代购是中间件,你是消息的接收方,你并不清楚这个键盘到底是哪家店的,那家店也并不清楚这个键盘最后是被谁买走了,键盘出了问题你都找不到店家售后。(不要跟我抬杠说现在代购也知道是在哪家店买的,我这个栗子是要说清楚发布/订阅的关键点)

所以前者应该是观察者模式,后者才是发布订阅模式。

回到程序中来,比方说一个应用区分登录状态和未登录状态的UI,很多不同的页面都可以实现登录的逻辑,在登录成功后,所有当前被打开的页面都需要更新UI。这种情况就可以使用发布/订阅模式,在某个页面登录成功后发布登录成功的事件,有中间件将这个事件分发给其他页面更新UI,注意其他页面关心的只是登录状态改变这件事,而不关心用户具体在哪个页面登录的

区别

这两种模式区别可以简单归结为是观察状态还是事件。

观察者模式中

  • 状态发布者维护了一个观察者的列表,明确的知道有哪些观察者存在,将状态变化直接通知给观察者
  • 状态的观察者也明确的知道自己观察的状态是描述的哪一个对象
  • 甚至需要这种相互知道的关系来处理逻辑(比如需要明确知道哪一个按钮被点击,处理对应的逻辑)

发布/订阅模式中

  • 事件的发布者只发布事件,不关心这个事件被谁获取了,通常将事件发给一个中间件,由中间件再去分发事件
  • 事件的订阅者只关心事件本身,不关心这个事件是谁发布的,通常在中间件中去注册观察某个事件
  • 中间件中去维护事件类别对应的订阅者列表,当收到事件后,去对应列表中通知订阅者们

场景

最重要的是知道什么场景应该用什么设计模式。可以按以下原则来判断

  • 是否观察的是状态(明确知道状态源)

如果被观察和观察双方需要明确知道对方,那就观察者模式,否则发布订阅模式

  • 一对一或者一对多的关系

这个事件或者状态只有一个发布者,两种都可以用,再参考第一条

  • 多对多的关系

首先所谓对多对的关系基本就可以确定传递的是事件,而不是状态,因为不同对象不应该发布相同的状态,不要犹豫选发布/订阅,如果你一定要用观察者模式来实现事件的传递,那么看下面这个图,耦合明白吗?另外考虑内存泄露反注册,用观察者模式你确定你都反注册完了?

多对多应用场景

写在最后

设计模式的学习不要局限在书本概念上,在写代码的过程中,学习优秀源码的过程中都可能在默默的接触不同的设计模式。设计模式本身就是在解决程序中的场景合理化问题,在实践中去理解不同的设计模式,甚至去思考现有模式的变种去解决实际的场景问题。

支持一下
如果对你有帮助,也可以支持下小站