观察者模式还是非常常用的,常见于基于zookeeper进行分布式系统之间的协调工作,比如分布式锁的注册以及监听是否释放。还有就是两个系统之间如果做了异步的处理,那么如果A系统发送异步请求给了B系统,但是要得到B系统的一个状态改变的消息,可以采用观察者模式。
创新互联建站主营长安网站建设的网络公司,主营网站建设方案,app软件开发,长安h5小程序定制开发搭建,长安网站营销推广欢迎长安等地区企业咨询
使用场景
在Zookeeper中的监听回调机制,以及分布式锁,都是使用了观察者模式。
基于zookeeper去做分布式锁
(1)系统A尝试获取zookeeper上的一个锁,获取到了
(2)系统B尝试获取zookeeper上的一个锁,被系统A给锁了,没有获取到锁,此时系统B在zookeeper上可以注册一个监听器(观察者)
(3)系统A一旦将锁给释放了,zookeeper感受到锁被释放了,就会立即通知系统B注册的那个监听器
(4)系统B就立即被通知到了,系统A释放了锁,系统B可以重新尝试在zookeeper上加锁
电商系统里,也有这种场景,如果两个系统之间走了异步请求,那么可以基于上面那种观察者模式现在一个进程内实现监听,以后拆分微服务分布式架构了,可以改成基于zookeeper来做分布式协调。
系统A发送了一条消息到内存队列,系统B获取了消息开始执行操作
但是系统A需要知道系统B的一个执行的结果如何,
此时系统A需要注册一个观察者到系统B上去,系统B执行完了之后,将执行的结果,反过来通知给系统
我们就可以基于观察者模式去做。
官方解释:
观察者模式(有时又被称为发布-订阅Subscribe模式、模型-视图View模式、源-收听者Listener模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。
个人理解:
观察者模式是一种思想,不需要人为的去关注观察者和被观察者之间是怎样联系的,实现了解耦,只需要对象去注册被观察者(Observerable)与观察者(Observer),然后被观察者去添加一个或者多个观察者,当被观察者发生变动就会立即通知所有的观察者,下面让我们来看看是怎样实现这个功能的。
被观察者首先通过addObserver(Observer o)来添加一个观察者,底层代码中会把这个对象o放进一个vector集合中,当然也可以添加多个观察者,当观察者发生变动的时候就会触发
setChanged();
notifyObservers();
这两个方法,然后底层代码中就回去遍历装有观察者的那个vector,然后
for (int i = arrLocal.length-1; i=0; i--)
((Observer)arrLocal[i]).update(this, arg);
调用update方法通知每一个观察者,这样观察者对象中就可以拿到被观察者的相关对象和信息
当我们想订一份报纸,我们先去邮局找到报纸的编号后填写订阅单并缴费。当报社有新报纸发出时,邮局会将我们订阅的报纸发给我们。
为了简单我们去掉邮局环节简化成:报社有新报纸后马上通知用户,这就是观察者。
定义对象间的一对多关系,当一个对象的状态发生变化时,所依赖于它的对象都得到通知并主动更新。 在观察者模式中,多个订阅者成为观察者(Observer),被观察的对象成为目标(Subject)。观察者的UML模型如下:
先定义Subject并写一个ConcreteSubject继承Subject:
再定义一个接口Observer,并写一个ConcreteObserver实现Observer接口:
最后看看主函数方法:
打印出来的结果:
在实现观察者模式的时候,一定要注意触发通知的时机。一般情况下是在完成了状态改变之后触发,因为通知会传递数据,比如在 setSubjectState 时先通知观测者就会发生 错误 。
在观察者模式的实现上,有推模式和拉模式两种方式:
当前上面的实现使用的就是拉模型。通过 (ConcreteSubject)subject 得到具体对象,获得信息。
当然Java本身就有观察者模式的部分实现,分别是 java.util.Observable java.util.Observable 。
下面看一个使用Java自带观察者模式的例子:
新的目标直接继承Java中定义的Observerable:
新的观察者也直接实现Observer接口:
主函数和前面的相似:
打印出结果:
使用Java自带的观察者模式需要注意以下几个问题:
观察者(Observer)模式又名发布-订阅(Publish/Subscribe)模式。GOF给观察者模式如下定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
在这里先讲一下面向对象设计的一个重要原则——单一职责原则。因此系统的每个对象应该将重点放在问题域中的离散抽象上。因此理想的情况下,一个对象只做一件事情。这样在开发中也就带来了诸多的好处:提供了重用性和维护性,也是进行重构的良好的基础。
因此几乎所有的设计模式都是基于这个基本的设计原则来的。观察者模式的起源我觉得应该是在GUI和业务数据的处理上,因为现在绝大多数讲解观察者模式的例子都是这一题材。但是观察者模式的应用决不仅限于此一方面。
下面我们就来看看观察者模式的组成部分。
1)抽象目标角色(Subject):目标角色知道它的观察者,可以有任意多个观察者观察同一个目标。并且提供注册和删除观察者对象的接口。目标角色往往由抽象类或者接口来实现。
2)抽象观察者角色(Observer):为那些在目标发生改变时需要获得通知的对象定义一个更新接口。抽象观察者角色主要由抽象类或者接口来实现。
3)具体目标角色(ConcreteSubject):将有关状态存入各个ConcreteObserver对象。当它的状态发生改变时,向它的各个观察者发出通知。
4)具体观察者角色(ConcreteObserver):存储有关状态,这些状态应与目标的状态保持一致。实现Observer的更新接口以使自身状态与目标的状态保持一致。在本角色内也可以维护一个指向ConcreteSubject对象的引用。
定义 :对象间的一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于他的对象都会得到通知,并自动更新。
交互对象之间松耦合
1)观察者定义了对象之间一对多的关系
2)被观察者用一个共同的接口来更新观察者
3)观察者和被观察者用松耦合方式结合,被观察者不知道观察者的细节,只知道观察者实现了观察者接口
优点:
1)观察者与被观察者抽象耦合,容易扩展;
2)建立了一套触发机制。
缺点:
1)循环依赖会导致系统崩溃;
2)观察者太多会浪费时间。
1)定义对象之间的一对多依赖关系而不使对象紧密耦合。
2)确保当一个对象改变状态时,自动更新开放数量的从属对象。
3)一个对象应该可以通知开放式数量的其他对象。
观察者模式 vs 发布-订阅模式
观察者模式(Observer)
如何使用 Java8 实现观察者模式?(上)
如何使用 Java8 实现观察者模式?(下)
spring 事件为bean 与 bean之间传递消息。一个bean处理完了希望其余一个接着处理.这时我们就需要其余的一个bean监听当前bean所发送的事件.
spring事件使用步骤如下:
1.先自定义事件:你的事件需要继承 ApplicationEvent
2.定义事件监听器: 需要实现 ApplicationListener
3.使用容器对事件进行发布
最后有一个思考 :ApplicationEvent事件执行部分和起一个TaskExecutor去执行 有啥区别吗?反正都是异步。
可以这样实现;
还可以这样实现;
我的思考:ApplicationEvent是观察者设计模式,这种设计模式使得主题和观察者之间的耦合度降低,松耦合是面向对象设计中很重要的一个原则,最终也是使用@Async来实现异步。而TaskExecutor则是启动一个线程池任务异步执行任务,两者效果一样,但原理不同。
通过我的思考,又带来一个疑问:那观察者模式是不是就是我们MQ中的发布订阅模式呢?只不过观察者模式是进程内的,而MQ是跨进程的?就这唯一的区别吗?
经过一些资料的查阅:大多数地方观察者模式 约等于 发布订阅模式,但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。
所以说观察者模式是小米加步枪,发布订阅模式是95式自动步枪,是它的进化版!