设计模式之观察者模式

作者: | 更新日期:

高大上的设计模式,走起。

本文首发于公众号:天空的代码世界,微信号:tiankonguse

一、背景

早在上大学的时候就看过不少设计模式的书籍,也看过不少设计模式文章。

但实际工作中,大部分在做业务需求,除了单例和工厂,其他设计模式就用的非常少了。

当做,做项目的时候,一直都是遵循高内聚低耦合思想来设计的。

最近再次看了一些设计模式书籍后,发现项目中已经无形用了很多很多设计模式了,只是不知道专业名字而已。

今天先分享一个高端的设计模式:观察者模式。

二、公众号

对于观察者模式,使用公众号这个产品来举例最恰当了。

在以前,我都是在自己的网站上写文章的。
地址是这里: https://github.tiankonguse.com

如果你意外遇到我的博客,发现我的文章对你有帮助。
并希望以后可以继续看我的新文章,你会怎么做?
肯定是随手添加到书签中了吧。

结果后来我更新了很多文章,你却不知道这件事。
于是你希望有一个功能,在我发表文章时,能够通知你。

于是你找到我的联系方式,让我更新文章的时候,给你微信上发个消息告诉你。
我答应了。

后来又有不少人这样让我通知他们,我就不干了,效率太低下了。
有没有什么工具帮我搞定这件事呢?

现在有公众号了,一切问题都解决了。

你可以订阅我的公众号。
这样我更新新文章的时候,公众号会自动给订阅的你们发送消息通知

这种通过订阅公众号,更新时自动通知所有订阅的人的方式,就是观察者模式。

当然,很多微信群、邮件组、rss邮件、feed邮件都是类似的模式。

三、消息队列

前面介绍过不少消息队列系统,都算是架构上通过观察者模式来解耦的。

比如《浅谈中转系统》、《简单粗暴的ZMQ通知中转》、《历史悠久的微博中转》、《问答看kafka》等等。

可以想象,你负责一个数据系统,有人希望数据变更的时候,你能够通知他们。

最原始的做法是在你的代码中增加一行代码,数据变更时,调用对方的接口。

后来有其他业务也需要订阅这个通知,你就又要发版本,调用新业务的接口。

这样下去肯定是不可持续的,所以需要想办法解耦。

此时消息队列系统就可以上场了。

你作为数据生产方,告诉消息队列系统自己要生产数据。
其他业务作为数据消费方,告诉消息队列系统自己要订阅这份数据。
消息队列系统就记录了你们这样的一对多关系。

当你有数据变更时,告诉消息队列,消息队列就会帮我们把数据转发给所有订阅的业务。

四、工程实现

上面介绍的两个例子虽然是观察者模式的思想,但是与工程上的具体实现还是有点差异的。

具体到一个工程内,一般是类与类之间的关系。

画出关系图大概就像下面的样子。

定义一个观察者接口(Observer),然后再定义一个通用的主题对象(Subject)。

希望订阅消息的类都继承 Observer,并实现约定好的方法 Update。
这样向 Subject 注册之后,有消息时,对应的 Update 函数会被 Subject 调用。

生产消息的类则需要继承 Subject,有消息的时候,调用父类的 Notify。
剩下的父类 Subject 都帮我自动完成了。

大概逻辑清楚了,那该如何实现呢?

Observer 类其实只有一个接口函数,相当简单。

子类继承 Observer 时实现 Update 函数即可。

主题 Subject 类做的事情比较多,需要使用一个容器把观察者存下来。
通知的时候,用来遍历调用 Update。

最后一步最关键,在运行程序的时候,消费者进行主题订阅。

这样,有消息的时候调用s.Notify()就会通知所有的观察者了。

五、问题

观察者模式将消息发布者与订阅者进行了解耦,解决了很多问题。

但是随着项目的演变,实际情况更复杂,也就暴露出了一些问题。

1、观察者依赖多个主题时,怎么区分主题?

这里需要有个参数来区分是哪个主题,我们在工作中一般预先分配唯一数字或者名字来标示。

2、订阅的多个主题之间有依赖,导致观察者收到多条更新。

这个问题工作中也遇到过。

上游数据CMS有很多计算模块,计算模块依赖于自身的基础信息或者其他计算的变更。
这就导致每个计算都会独立的发出一个通知。

对外表现就是,只修改一个数据,最后发出了几十条数据变更消息。

更可怕的时候,有个业务的依赖计算形成了环,正常情况下没问题,异常情况下则直接导致死循环造成滚雪球雪崩。

这个比较好的方法是内部的计算对外不可见,对外只发出一个通知。

3、推还是拉

公众号就是一种推得观察者模式,而kafka消息队列则是拉的观察者模式。

具体要用推还是拉,需要根据实际情况来判断。

推的话,由于不知道对方使用什么数据,就需要将所有数据推过去(当然可以注册的时候登记需要什么)。
拉的话可以按需拉取,甚至合并消息最拉取一次消息。

4、按需通知

其实上一小段已经提到了,有时候并不需要所有的消息。

这个时候就需要支持对消息的细分,然后按需进行订阅与消费。

六、最后

写到最后,我突然想起来,在2015年的时候,我负责的一个项目中大量的使用了观察者模式。

大概是2015年,职责调整,一个网站拆分为后台和前端。
后台交给我负责,前端给一个前端大佬负责。

而那个网站非常复杂,前端大佬维护起来也相当痛苦。
于是就用了一段时间把网站进行了重构。
结果两个月后,职责又调整,前端就也交接给我负责了。

当时一看代码傻眼了,各种add_observenotify,我数了数竟然有七八个类互相订阅对方。

找到了当时的 js 代码,也分享给大家。

思考题:你用过观察者模式吗?

《完》

-EOF-

本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号ID:tiankonguse-code

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越