观察者模式
一.概述
观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
二. 解决的问题
将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
三.模式与角色
-
抽象主题(Subject)角色:主题角色把所有的观察者对象的引用保存在一个列表里;每个主题都可以有任何数量的观察者。主题提供一个接口可以加上或撤销观察者对象;主题角色又叫做抽象被观察者(Observable)角色;
-
具体主题(ConcreteSubject):保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫作具体被观察者角色;。
-
抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
-
具体观察者(ConcreteObserver):具体观察者角色实现抽象观察者角色所要求的更新自己的接口,以便使本身的状态与主题的状态自恰。
抽象主题(Subject)角色
import java.util.ArrayList; import java.util.List; public abstract class Subject { private List<Observer> observers = new ArrayList<Observer>(); // 增加观察者 public void Attach(Observer observer) { observers.add(observer); } // 移除观察者 public void Detach(Observer observer) { observers.remove(observer); } // 向观察者(们)发出通知 public void Notify(Object args) { for (Observer observer : observers) { observer.update(args); } } }
抽象观察者类
//抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己 public interface Observer { public abstract void update(Object object); }
具体主题类
//在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。 public class ConcreteSubject extends Subject { private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; } }
具体观察者类
// 具体观察者,实现抽象观察者角色所要求的更新接口 public class ConcreteObserver implements Observer { private String name; public ConcreteObserver(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } // 实现抽象观察者中的更新操作 @Override public void update(Object object) { System.out.println(name + "的state变为" + object); } }
场景类
class Client { public static void main(String[] args) { // 具体主题角色通常用具体自来来实现 ConcreteSubject subject = new ConcreteSubject(); subject.Attach(new ConcreteObserver("Observer A")); subject.Attach(new ConcreteObserver("Observer B")); subject.Attach(new ConcreteObserver("Observer C")); subject.setSubjectState("Ready"); subject.Notify(subject.getSubjectState()); subject.setSubjectState("Finish"); subject.Notify(subject.getSubjectState()); } }
执行结果
Observer A的state变为Ready Observer A的state变为Finish |
四. 模式总结
4.1优点
-
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
4.2缺点
-
依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。
4.3适用场景
-
当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。
-
一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
五.Java语言提供的对观察者模式的支持
在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成Java语言对观察者模式的支持。
这个接口只定义了一个方法,update()。当被观察者对象的状态发生变化时,这个方法就会被调用。这个方法的实现应当调用每一个被观察者对象的notifyObservers()方法,从而通知所有的观察对象。
java.util提供的Observer接口的类图
java.util.Observer接口的源代码
package java.util; public interface Observer { /** * 当被观察的对象发生变化时,这个方法会被调用。 */ void update(Observable o, Object arg); }
Observable类
被观察者类都是java.util.Observable类的子类。java.util.Observable提供公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要:一个是setChanged(),另一个是notifyObservers()。第一个方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。
java.util.Observable类还有其它的一些重要的方法。比如,观察者对象可以调用java.util.Observable类的addObserver()方法,将对象一个一个加入到一个列表上。当有变化时,这个列表可以告诉notifyObservers()方法那些观察者对象需要通知。由于这个列表是私有的,因此java.util.Observable的子对象并不知道观察者对象一直在观察着它们。
Java语言提供的被观察者的类图。
被观察者类Observable的源代码:
package java.util; public class Observable { private boolean changed = false; private Vector obs; /** *//** 用0个观察者构造一个被观察者。**/ public Observable() { obs = new Vector(); } /** *//** * 将一个观察者加到观察者列表上面。 */ public synchronized void addObserver(Observer o) { if (!obs.contains(o)) { obs.addElement(o); } } /** *//** * 将一个观察者对象从观察者列表上删除。 */ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } /** *//** * 相当于 notifyObservers(null) */ public void notifyObservers() { notifyObservers(null); } /** *//** * 如果本对象有变化(那时hasChanged 方法会返回true) * 调用本方法通知所有登记在案的观察者,即调用它们的update()方法, * 传入this和arg作为参量。 */ public void notifyObservers(Object arg) { /** *//** * 临时存放当前的观察者的状态。参见备忘录模式。 */ Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } /** *//** * 将观察者列表清空 */ public synchronized void deleteObservers() { obs.removeAllElements(); } /** *//** * 将“已变化”设为true */ protected synchronized void setChanged() { changed = true; } /** *//** * 将“已变化”重置为false */ protected synchronized void clearChanged() { changed = false; } /** *//** * 探测本对象是否已变化 */ public synchronized boolean hasChanged() { return changed; } /** *//** * 返还被观察对象(即此对象)的观察者总数。 */ public synchronized int countObservers() { return obs.size(); } }
这个Observable类代表一个被观察者对象。一个被观察者对象可以有数个观察者对象,一个观察者可以是一个实现Observer接口的对象。在被观察者对象发生变化时,它会调用Observable的notifyObservers方法,此方法调用所有的具体观察者的update()方法,从而使所有的观察者都被通知更新自己。见下面的类图:
发通知的次序在这里没有指明。Observerable类所提供的缺省实现会按照Observers对象被登记的次序通知它们,但是Observerable类的子类可以改掉这一次序。子类并可以在单独的线程里通知观察者对象;或者在一个公用的线程里按照次序执行。
当一个可观察者对象刚刚创立时,它的观察者集合是空的。两个观察者对象在它们的equals()方法返回true时,被认为是两个相等的对象。
六.java观察者模式例子
观察者模式实现房价变化自动通知购房者
import java.util.* ; class House extends Observable{ // 表示房子可以被观察 private float price ;// 价钱 public House(float price){ this.price = price ; } public float getPrice(){ return this.price ; } public void setPrice(float price){ // 每一次修改的时候都应该引起观察者的注意 super.setChanged() ; // 设置变化点 super.notifyObservers(price) ;// 价格被改变 this.price = price ; } public String toString(){ return "房子价格为:" + this.price ; } }; class HousePriceObserver implements Observer{ private String name ; public HousePriceObserver(String name){ // 设置每一个购房者的名字 this.name = name ; } public void update(Observable o,Object arg){ if(arg instanceof Float){ System.out.print(this.name + "观察到价格更改为:") ; System.out.println(((Float)arg).floatValue()) ; } } }; public class ObserDemo01{ public static void main(String args[]){ House h = new House(1000000) ; HousePriceObserver hpo1 = new HousePriceObserver("购房者A") ; HousePriceObserver hpo2 = new HousePriceObserver("购房者B") ; HousePriceObserver hpo3 = new HousePriceObserver("购房者C") ; h.addObserver(hpo1) ; h.addObserver(hpo2) ; h.addObserver(hpo3) ; System.out.println(h) ; // 输出房子价格 h.setPrice(666666) ; // 修改房子价格 System.out.println(h) ; // 输出房子价格 } };
上面的设置相同的值会通知,如h.setPrice(1000000),可以事先对price进行判断
if ( this.price!=price ) { super.setChanged() ; // 设置变化点 super.notifyObservers(price) ;// 价格被改变 this.price = price ; }