参考资料:
https://www.imooc.com/learn/415
https://blog.csdn.net/itachi85/article/details/50773358
观察者模式模式实践代码
目录
1、什么是观察者模式
2、观察者模式的2种实现方式
3、观察者模式的2种实现方式的比较
4、观察者模式的的角色
5、观察者模式的优缺点
6、观察者模式的应用场景
7、观察者模式的衍生
8、观察者模式的结构
9、观察者模式的通用代码
10、观察者模式的本质
11、从六大方面观察者模式
12、利用JAVA提供的观察者实现
1、什么是观察者模式
定义:
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
其他定义参考:
定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新;
注:一对一属于一对多;
一个目标可以有任意多个观察者对象,一但目标的状态发生了改变,所有注册的观察者都会得到通知,各个观察者会对通知做出相应的响应,执行相应的业务逻辑处理;
2、观察者模式的2种实现方式
(1)推模型
目标对象主动向观察者推送目标的详细信息,推送的信息通常是目标对象的全部或部分数据;
注:不管观察者是否需要 ,推送的信息通常是目标对象的全部或部分数据;
(2)拉模型
目标对象在通知观察者的时候,只传递少量信息。
如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。
一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者。
3、观察者模式的2种实现方式的比较
- 推模型是假定目标对象知道观察者需要的数据;
- 拉模型是目标对象不知道观察者具体需要什么数据,因此把自身传给观察者,由观察者来取值;
- 推模型会使观察者对象难以复用
解释:因为观察者定义update()方法,是按需来定制的,可能无法兼顾没有考虑到的情况,这就意味着出现新情况的时候,就可能需要提供新的Update方法,或者干脆重新实现观察者,而拉模型就不会造成这样的情况; - 拉模型下,update方法的参数是目标对象本身,基本上可以适应各种情况的需要;
4、观察者模式的的角色
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
5、观察者模式的优缺点
优点:
第一、观察者模式实现了观察者和目标之间的抽象耦合
解释:原本目标对象在状态发生改变的时候,需要直接调用所有观察者对象,但是抽象出观察者接口以后,目标和观察者就只是在抽象层面上耦合,也就是说目标只是知道观察者的接口,并不知道具体的观察者的类,从而实现了目标类和具体的观察者之间的
第二、观察者模式实现了动态联动
解释:所谓联动,就是做一个操作,会引起其他相关的操作,由于观察者模式对注册实行管理,那就可以在运行期间通过动态的控制,注册的观察者来控制 某个动作的联动范围,从而实现动态联动。
第三、观察者模式支持广播通信
解释:由于目标发送通知给观察者,是面向所有注册的观察者,所以每次目标通知的信息就要对所有注册的观察者进行保护,当然也可以在目标上添加新的功能来限制广播的范围
缺点:
可能会引起无谓的操作;
解释:
由于观察者每次都是广播通信,不管观察者是不是需要,每个观察者都会被调用update方法,若观察者不需要执行相应的处理,那么这些操作是不是就浪费了,其实浪费是新的问题,最怕引起误会,就麻烦了。
其实模式的应用,就像孙子兵法的36计,具体用在什么样的战场上,就要看用计的人能不能熟练应用。
在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
6、观察者模式的应用场景
- 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
建议在以下情况中选用观察者模式:
第一 当一个抽象模型有2个方面,其中一个方面的操作依赖于另一个方面的状态变化;
解释:将这2者封装成观察者和目标对象,当目标对象变化的时候,依赖于它的观察者对象也会发生相应的变化,这样就可以把抽象模型的这两个方面分离开,使得他们可以独立的改变、复用
第二 如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变
注: 这种情况,被更改的对象很明显是目标对象,而需要连带修改的其他对象就可以当作是多个观察者对象
第三 当一个对象必须通知其他的对象,但你又希望这个对象和其他被它通知的对象是松散耦合的;
注:这个对象不想知道具体被通知的对象,这种情况可以选用观察者模式,这个对象就相当于是目标对象,而被他通知的对象就是观察者对象;
7、观察者模式的衍生
TODO
8、观察者模式的结构
9、观察者模式的通用代码
步骤:
1)、是目标对象的定义
2)、是具体的目标对象的定义
3)、再来看看观察者的接口定义
4)、看看观察者的具体实现
10、观察者模式的本质
当修改目标对象的状态的时候,就会触发相应的通知,然后会循环调用所有注册观察者对象相应的方法,其实就相当于联动调用这些观察者的方法
理解观察者模式的本质很重要,对我们识别和使用观察者模式具有重要的意义,尤其是在练习使用的时候万变不离其宗
11、从六大方面观察者模式
(1)目标与观察者之间的关系
按照模式的定义,目标与观察者之间是联系的,是一对多的关系;
一个观察者可以对多个目标;
如果观察者为多个目标定义了通知,更新的方法都是update方法的话,这样会带来麻烦需要接收多个目标的通知,若是一个update方法,需要在方法内部区分。一般情况下观察者应该为不同的观察者目标立不同的观察方法;
(2)单向依赖
在观察者模式中,观察者和目标是单向依赖的,只有观察者依赖目标,而不是目标依赖观察者,他们之间的联系,主动权掌握在目标者的手中,只有目标知道什么时候需要通知观察者,在整个过程中观察者是被动的,被动的等待目标的通知,等待目标传值给他;
(3)命名建议
1)目标接口的定义,建议在名称后面跟Subject;
2)观察者接口的定义,建议在名称后面跟Observer;
3)观察者接口的更新方法,建议名称为update
(4)触发通知的时机
在实现观察者模式的时候,一定要注意触发通知的时机,一般情况下是在完成状态维护之后触发,因为通知会传递数据,不能先通知后改变数据,这很容易出问题,会导致观察者目标和对象状态不一致;
(5)观察者模式的调用顺序
TODO
(6)通知的顺序
从理论上来说,当目标对象的状态变化后,通知所有的观察者的时候,需求是不确定的,因此观察者实现的功能绝对不能依赖于通知的顺序,也就是说,多个观察者之间的顺序是平行的,相互不应该有先后依赖的关系
12、利用JAVA提供的观察者实现
JAVA实现与自己实现的对比(四点):
(1)不需要再定义观察者和目标的接口了,JDK帮忙定义了;
(2)具体的目标实现里面不需要再维护观察者的注册信息了,这个在java中的Observable类里面已经帮忙实现好了;
(3)触发通知的方式有一点变化,要先调用setChanged方法,这个是JAVA为了帮助实现更精确的触发控制而提供的功能;
(4)具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个是JAVA在定义的时候,就已经考虑进去了;
网友评论