什么是观察者模式,Wiki 上的定义为:
观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。
这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。
通过定义也可以看出,一个对象是可以对应多个观察者的,当这个对象发生变化时,会通知所有的观察者,让它们能够自动更新自己
观察者还是 JDK 中使用最多的模式之一,相当于定义了对象之间的一对多依赖
角色
观察者模式涉及到了4个角色:
- 抽象目标(Subject)角色:
此抽象类提供一个界面让观察者进行添附与解附作业。通俗理解就是主要定义三个功能的方法,注册、删除、更新(通知)观察者 - 具体目标(ConcreteSubject)角色:
将有关状态存入具体观察者对象;在具体目标的内部状态改变时,给所有登记过的观察者发出通知。 - 抽象观察者(Observer)角色:
为所有的具体观察者定义一个接口,在得到目标(被观察者)的通知时更新自己,这个接口叫做更新接口。 - 具体观察者(ConcreteObserver)角色:
具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与目标(被观察者)的状态相协调。如果需要,具体观察者角色可以保持一个指向具体目标对象的引用。每个观察者类别都要实做它自己的更新函式,以应对状态更新的情形。
可以看出,我们常说的“回调机制”就是它的一种体现形式嘛~~~回调的对象其实就是一个观察者嘛 (: 雾
一个栗子
下面就开始具体写这四个角色了,具体的写法不是固定的,我也看到了好多不同的写法,思路是差不多的
观察者模式的代表人物就是 MVC 了!!
抽象目标角色
首先写抽象目标角色吧,正如上面所说,主要定义的是对观察者的一些列操作,我呢,继续偷懒尽可能复用前面的代码了 o(*≧▽≦)ツ
或者你可以按照 HeadFirst 中的叫法:主题(抽象);想象成出版社?负责内容的更新以及通知相应的订阅者
1 | public interface StandardLoli { |
看到网上的一些其他实现是把这个类定义为抽象类,然后把上面的方法给实现了,因为所有的被观察者的实现基本都是一个套路
具体目标角色
也就是所说的 被观察者 了,我是在这里实现的抽象目标的方法,register 等方法是给 new 出的具体的对象调用的
就是上面所说的主题的具体的实现了。相当于具体的某家出版社
这个类通常会有一个属性表示状态….这里没写
1 | public class StandardLoliImpl implements StandardLoli { |
notifyObservers 的参数应该是接口的,我就是图方便直接写的实现类啦~
如果是使用的抽象类的方式,这里只要定义一个状态标志变量和 change 方法(相当于我的 speak 方法)就行了,具体的可以见参考里的第二个连接
抽象观察者角色
这个才是最简单的一个接口,就一个方法,用来更新观察者状态的
1 | public interface Lolicon { |
观察者角色
被观察者状态变化后调用,用来通知观察者进行相应的更新自己
1 | public class LoliconImpl implements Lolicon { |
测试类
好了,所有的角色都定义完毕,下面就开始操练起来了
1 | public class MainTest { |
还有一点,很多人 (反正我是…..)都不会这样严格的定义,定义四个类太麻烦啦,我嘛,简单的功能就会省略掉抽象目标角色了…
果然是不是非常像我们常用的“回调机制”
使用Java的默认实现
其实在 java 中实现观察者模式还有一种更简单的方式,它已经给我们提供好类使用了。
需要用到 java.util
包中提供的 Observable 类和 Observer 接口,分别对应 被观察者、观察者
被观察者需要继承 Observable ;它已经给你写好注册、取消等方法了,省事了不少,直接 super 调就行,不过需要注意的是 setChanged 方法,如果你不调用这个告诉它数据已经变化,notifyObservers 是不会执行的
这样其实有个好处就是避免了频繁的更新
1 | public class StandardLoli extends Observable { |
Observable 内部保存多个观察者的是一个 AbstractList 集合,看一下源码应该已经保证了线程安全,但是因为它和 ArrayList 的存储结构是一样的,如果需要频繁操作还是要用链表
notifyObservers 方法可以传一个 data 数据,这相当于是“推”;如果不传就相当于“拉”;所以,Java 的实现是支持这两种方式的
然后就是观察者了,需要实现 Observer 接口,重写 update 方法
1 | public class Lolicon implements Observer { |
其实在这里的构造函数中可以接收一个被观察者,直接将本对象注册,这好像才是比较正确的使用姿势……
然后写个测试类来测试下,看看效果:
1 | public class Test { |
嗯,这样看的话确实简单了不少,只需要两个类呢
使用 Java 的实现时;不要依赖于观察者被通知的次序
因为 notifyObservers 是 Java 它实现的,具体的实现思路可能和我们自己的并不相同
参考
这里推荐一篇文章:
https://www.zybuluo.com/pastqing/note/191632
http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html
评论框加载失败,无法访问 Disqus
你可能需要魔法上网~~