在阎宏博士的《JAVA与模式》一书中开头是这样描述观察者(Observer)模式的:观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式的结构
一个软件系统通常要求在某个对象的状态变化时,其他的某些对象随之变化。观察者模式是满足这一要求的所有方案中最好的一种。结构图如下:
涉及的角色:
抽象主题(Subject)角色:抽象主题角色又叫做抽象被观察者(Observable)角色。定义一个可以增加和删除观察者对象的接口,每个主题把所有观察者对象的引用保存在一个集合(例如ArrayList)里面。
具体主题(ConcreteSubject)角色:具体主题角色又叫做具体被观察者(Concrete Observable)角色。在具体主题的状态变化时,给所有登记过的观察者发出通知。
抽象观察者(Observer)角色:为所有的具体观察者定义一个更新接口,收到主题的通知时更新具体观察者。
具体观察者(ConcreteObserver)角色:存储与主题状态适应的状态,实现了抽象观察者角色的更新接口。
抽象主题角色类
1 public abstract classSubject {2 /**
3 * 用来保存注册的观察者对象4 */
5 private List list = new ArrayList();6 /**
7 * 注册观察者对象8 *@paramobserver 观察者对象9 */
10 public voidattach(Observer observer){11
12 list.add(observer);13 System.out.println("Attached an observer");14 }15 /**
16 * 删除观察者对象17 *@paramobserver 观察者对象18 */
19 public voiddetach(Observer observer){20
21 list.remove(observer);22 }23 /**
24 * 通知所有注册的观察者对象25 */
26 public voidnodifyObservers(String newState){27
28 for(Observer observer : list){29 observer.update(newState);30 }31 }32 }
具体主题角色类
1 public class ConcreteSubject extendsSubject{2
3 privateString state;4
5 publicString getState() {6 returnstate;7 }8
9 public voidchange(String newState){10 state =newState;11 System.out.println("主题状态为:" +state);12 //状态发生改变,通知各个观察者
13 this.nodifyObservers(state);14 }15 }
抽象观察者角色类
1 public interfaceObserver {2 /**
3 * 更新接口4 *@paramstate 更新的状态5 */
6 public voidupdate(String state);7 }
具体观察者角色类
1 public class ConcreteObserver implementsObserver {2 //观察者的状态
3 privateString observerState;4
5 @Override6 public voidupdate(String state) {7 /**
8 * 更新观察者的状态,使其与目标的状态保持一致9 */
10 observerState =state;11 System.out.println("状态为:"+observerState);12 }13
14 }
客户端类
1 public classClient {2
3 public static voidmain(String[] args) {4 //创建主题对象
5 ConcreteSubject subject = newConcreteSubject();6 //创建观察者对象
7 Observer observer = newConcreteObserver();8 //将观察者对象登记到主题对象上
9 subject.attach(observer);10 //改变主题对象的状态
11 subject.change("new state");12 }13
14 }
结果:
客户端先创建具体主题类的实例和一个观察者对象。然后,它调用主题对象的attach方法,将观察者对象登记到主题对象上,即把它加入到主题对象的集合中。客户端调用主题对象的change方法后改变了主题对象的状态。主题对象在状态变化时,调用父类的notifyObservers方法,通知所有登记过的观察者对象,更新它们的状态。
推模型和拉模型
观察者模式分为推模型和拉模型两种方式。
推模型:不管观察者对象是否需要,主题对象都会向观察者对象推送主题的详细信息。
拉模型: 主题对象在通知观察者对象时,只传递少量信息。如果观察者对象需要更具体的信息,观察者对象需要主动向主题对象获取,相当于观察者对象从主题对象中拉数据。通过update方法把主题对象传递给观察者对象,通过主题对象获取信息。
前面的例子是推模型,下面给出一个拉模型的实例:
拉模型的抽象观察者类
1 public interfaceObserver {2 /**
3 * 更新接口4 *@paramsubject 传入主题对象,方面获取相应的主题对象的状态5 */
6 public voidupdate(Subject subject);7 }
拉模型的具体观察者类
1 public class ConcreteObserver implementsObserver {2 //观察者的状态
3 privateString observerState;4
5 @Override6 public voidupdate(Subject subject) {7 /**
8 * 更新观察者的状态,使其与目标的状态保持一致9 */
10 observerState =((ConcreteSubject)subject).getState();11 System.out.println("观察者状态为:"+observerState);12 }13
14 }
拉模型的抽象主题类
1 public abstract classSubject {2 /**
3 * 用来保存注册的观察者对象4 */
5 private List list = new ArrayList();6 /**
7 * 注册观察者对象8 *@paramobserver 观察者对象9 */
10 public voidattach(Observer observer){11
12 list.add(observer);13 System.out.println("Attached an observer");14 }15 /**
16 * 删除观察者对象17 *@paramobserver 观察者对象18 */
19 public voiddetach(Observer observer){20
21 list.remove(observer);22 }23 /**
24 * 通知所有注册的观察者对象25 */
26 public voidnodifyObservers(){27
28 for(Observer observer : list){29 observer.update(this);30 }31 }32 }
拉模型的具体主题类
1 public class ConcreteSubject extendsSubject{2
3 privateString state;4
5 publicString getState() {6 returnstate;7 }8
9 public voidchange(String newState){10 state =newState;11 System.out.println("主题状态为:" +state);12 //状态发生改变,通知各个观察者
13 this.nodifyObservers();14 }15 }
两种模式的比较
推模型是假设主题对象知道观察者对象需要的数据;而拉模型是主题对象不知道观察者对象需要什么数据。
推模型可能会使观察者对象难以复用,因为观察者对象的update方法是按需要定义形参,可能无法兼顾没有考虑到的使用情况。当新情况出现时,就可能需要提供新的update方法,或者重新实现观察者类;而拉模型不会出现这种情况,因为观察者对象的update方法的形参是主题对象,通过主题对象可以获取所有信息。
Java对观察者模式的支持
java.util库提供了一个Observer接口和一个Observable类。
Observer接口
只定义了一个update方法。当被观察者对象的状态变化时,被观察者对象的notifyObservers方法就会调用该方法。
1 public interfaceObserver {2 voidupdate(Observable o, Object arg);3 }
Observable类
被观察者类相当于抽象主题类,是java.util.Observable类的子类。有两个方法对Observable的子类来说很重要:一个是setChanged方法,被调用后会设置一个标记变量,代表被观察者对象的状态变化了。第二个是notifyObservers方法,被调用时会调用所有登记过的观察者对象的update方法,更新观察者对象。
1 public classObservable {2 private boolean changed = false;3 privateVector obs;4
5 /**Construct an Observable with zero Observers.*/
6
7 publicObservable() {8 obs = newVector();9 }10
11 /**
12 * 将一个观察者添加到观察者聚集上面13 */
14 public synchronized voidaddObserver(Observer o) {15 if (o == null)16 throw newNullPointerException();17 if (!obs.contains(o)) {18 obs.addElement(o);19 }20 }21
22 /**
23 * 将一个观察者从观察者聚集上删除24 */
25 public synchronized voiddeleteObserver(Observer o) {26 obs.removeElement(o);27 }28
29 public voidnotifyObservers() {30 notifyObservers(null);31 }32
33 /**
34 * 如果本对象有变化(那时hasChanged 方法会返回true)35 * 调用本方法通知所有登记的观察者,即调用它们的update()方法36 * 传入this和arg作为参数37 */
38 public voidnotifyObservers(Object arg) {39
40 Object[] arrLocal;41
42 synchronized (this) {43
44 if (!changed)45 return;46 arrLocal =obs.toArray();47 clearChanged();48 }49
50 for (int i = arrLocal.length-1; i>=0; i--)51 ((Observer)arrLocal[i]).update(this, arg);52 }53
54 /**
55 * 将观察者聚集清空56 */
57 public synchronized voiddeleteObservers() {58 obs.removeAllElements();59 }60
61 /**
62 * 将“已变化”设置为true63 */
64 protected synchronized voidsetChanged() {65 changed = true;66 }67
68 /**
69 * 将“已变化”重置为false70 */
71 protected synchronized voidclearChanged() {72 changed = false;73 }74
75 /**
76 * 检测本对象是否已变化77 */
78 public synchronized booleanhasChanged() {79 returnchanged;80 }81
82 /**
83 * Returns the number of observers of this Observable object.84 *85 *@returnthe number of observers of this object.86 */
87 public synchronized intcountObservers() {88 returnobs.size();89 }90 }
如何使用?
被观察者类Watched
1 public class Watched extendsObservable{2
3 private String data = "";4
5 publicString getData() {6 returndata;7 }8
9 public voidsetData(String data) {10
11 if(!this.data.equals(data)){12 this.data =data;13 setChanged();14 }15 notifyObservers();16 }17
18
19 }
观察者类Watcher
1 public class Watcher implementsObserver{2
3 publicWatcher(Observable o){4 o.addObserver(this);5 }6
7 @Override8 public voidupdate(Observable o, Object arg) {9
10 System.out.println("状态发生改变:" +((Watched)o).getData());11 }12
13 }
测试类Test
public classTest {public static voidmain(String[] args) {//创建被观察者对象
Watched watched = newWatched();//创建观察者对象,并将被观察者对象登记
Observer watcher = newWatcher(watched);//给被观察者状态赋值
watched.setData("start");
watched.setData("run");
watched.setData("stop");
}
}
先创建Watched和Watcher对象。在创建Watcher对象时,将Watched对象作为参数传入。然后Watched对象调用setData方法,更改Watched对象的状态。Watched对象通知登记过的Watcher对象,调用它的update方法。
参考资料