定义
观察者模式是对象的行为模式。
观察者模式定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
结构和角色
主题角色 : Subject是一个接口,定义了增加,移除观察者和通知观察者方法。
观察者角色 : Observer是一个接口, 为所有具体观察者定义了更新的方法,当主题角色(被观察者)状态发生改变,会调用此方法。
具体主题角色 : ConcreteSubject,实现主题角色接口,管理观察者集合;将有关状态存入具体观察者,在具体主题的内部状态改变时,给所有登记过的观察者发出通知(即调用update方法)。
具体观察者角色 : ConcreteObserver 实现Observer接口。接受主题角色的通知。
代码.....
问题
如果有多个具体主题角色(被观察者角色),那么管理观察者集合的方法和通知观察者方法是不是会重复?
从以上实现来看,主题角色是一个接口,所以每一个具体主题角色都要实现主题角色接口的方法。观察者有共同的(Observer)接口,具体主题角色里定义的是观察者接口的集合。所以每一个具体主题角色在管理观察者集合的方法和通知观察者方法都是相同的。这就造成了代码的重复。
解决以上问题的方法(也是Java内置的观察者模式)
原来的主题角色接口由一个类来代替,将管理观察者集合和通知观察者的方法提到了Observerable类里面。具体的主题角色只需继承此类即可。当状态发生改变,只需调用父类的notifyObserver方法。
java.util.Observable源码里面有个setChanged()方法,这个方法有个专用词"钩子方法",我的印象中在struts1的源码里面用过这种方法。这种方法在控制状态改变通知观察者时有更好的弹性。
代码.....
比较这两种实现方法:
第一种方式,由于主题角色使用接口,那么多个具体主题角色在管理观察者集合上代码重复。
第二种方式,采用类继承。解决了第一种方式代码重复的问题。但是这种方法有一个很大的弊端,由结构图可以看出,主题角色是一个类不再是接口,因为是类,所以必须具体主题角色必须采用继承。因为java是单继承,所以如果具体主题角色还要继承其他的类,这就实现不了了。还有一个问题,setChanged()方法是protected修饰的,所以除非你继承Observable,否则你无法创建Observable实例并组合到自己的对象中,这种方式并不符合面向接口编程。
任何事都没有绝对的标准,具体问题具体分析。
实例
观察者模式在Java中用的相当广泛,例如
- MVC架构中,就用到了观察者模式, MC 是主题角色 V 是观察者角色。
- 几乎所有的事件监听都采用了观察者模式,如 button 被双击 会调用 clickListener 方法..等。
- servlet 中的事件处理机制。
优缺点
优点
观察者模式在被观察者和观察者之间松耦合。
支持广播通信,被观察者会向所有的登记过的观察者发出通知。
缺点
--
设计原则
组合优于继承,观察者模式利用"组合"将许多观察者组合进主题中。
封装变化,变化的是主题的状态和观察者的数目和类型。
面向接口编程,主题和观察者都使用接口。