和其它形式的回调函数(callback)类似,Observer模式也允许你通过挂钩程序(hook
point)改变代码。不同之处在于,从本质上说,Observer模式是完全动态的。它经常被用于需要根据其它对象的状态变化来改变自身(状态)的场
合,而且它还经常是事件管理系统(event
management)的基本组成部分。无论什么时候,当你需要用完全动态的方式分离呼叫源和被呼叫代码的时候,(Observer模式都是你的首选)。
(译注:作者最后一句话好像没写完 )
Observer模式解决的是一个相当常见的问题:当某个对象改变状态的时候,另外一组(与之相关的)对象如何更新它们自己。比如说,Smalltalk
里的“model-view”结构,(它是MVC(model-view-controller)结构的一部分),再比如基本与之相当的“文档-视图
(document-view)”结构。假设说你有一些数据(也就是“文档”)和多于一个的视图,比如说是一个图表(plot)和一个文本视图。当你改变
数据的时候,这两个视图必须知道进而(根据需要)更新它们自己,这也就是Observer模式所要帮你解决的问题。这个问题是如此的常见,以至于它的解决
办法已经成了标准java.util库的一部分。
用Java实现observer模式需要用到两种类型的对象,Observable
类负责记住发生变化时需要通知哪些类,而不论“状态”改变与否。当被观察对象说“OK,你们(指观察者)可以根据需要更新你们自己了,”
Observable类通过调用notifyObservers(
)方法通知列表上的每个观察者,进而完成这个任务。notifyObservers( )是基类Observable的一个方法。
实际上,Observer模式真正变化的有两样东西:观察者(observing objects)的数量和它们如何更新自己。也就是说,observer模式使得你在不必改动其它代码的情况下只针对这两种变化更改代码。
。。。。。。(译注:作者还没写完 )
Observer实际上是只有一个成员函数的接口类,这个成员函数就是update(
)。当被观察者决定更新所有的观察者的时候,它就调用update( )函数。是否需要传递参数是可选的;即使是没有参数的Update(
)函数也同样符合observer模式;但是,更通常的做法是让被观察者(通过update( )函数)把引起更新的对象(也就是它自己
)和其它有用的信息传递给观察者,
因为一个观察者可能会注册到多于一个的被观察者。这样,观察者对象就不用再费劲查找是哪个被观察者引起的更新,并且它所需要的信息也已经传递过来。
决定何时以及如何发起更新(updating)的那个“被观察者对象”被命名为Observable。
Observable
类用一个标志(flag)来指示它自己是否改变。对于比较简单的设计来说,不用flag也是可以的;如果有变化,就通知所有的观察者。如果用flag的
话,你可以使通知的时间延迟,并且由你来决定只在合适的时候通知观察者。但是,请注意,控制flag状态的方法是受保护的(protected),也就是
说,只有(Observable类的)派生类可以决定哪些东西可以构成一个变化,而不是由Observer派生类的最终用户来决定。
大多数工作是在notifyObservers( )这个方法里完成的。如果没有将flag置为“已改变”,那notifyObservers(
)什么也不做;否则,它先清除flag的“已改变”状态,从而避免重复调用notifyObservers(
)的时候浪费时间。这些要在通知观察者之前完成,为了避免对于update( )的调用有可能引起被观察对象的一个反向的改变。
然后notifyObservers()方法就遍历它所保存的观察者序列,并且调用每个观察者的update( )成员函数。
初看起来,似乎可以用一个普通的Observable对象来管理更新。但是实际上办不到;为了达到这个效果,你必须继承Observable类,并且在派
生类的代码里调用setChanged(
)方法。它就是用来将flag置为“已改变”的那个成员函数,这么一来,当你调用notifyObservers(
)的时候所有的观察者都会被准确无误的通知道。在什么地方调用setChanged( ),这取决于你程序的逻辑结构。
观察花朵
//: observer:ObservedFlower.java
// Demonstration of "observer" pattern.
package observer;
import java.util.*;
import junit.framework.*;
class Flower {
private boolean isOpen;
private OpenNotifier oNotify =
new OpenNotifier();
private CloseNotifier cNotify =
new CloseNotifier();
public Flower() { isOpen = false; }
public void open() { // Opens its petals
isOpen = true;
oNotify.notifyObservers();
cNotify.open();
}
public void close() { // Closes its petals
isOpen = false;
cNotify.notifyObservers();
oNotify.close();
}
public Observable opening() { return oNotify; }
public Observable closing() { return cNotify; }
private class OpenNotifier extends Observable {
private boolean alreadyOpen = false;
public void notifyObservers() {
if(isOpen && !alreadyOpen) {
setChanged();
super.notifyObservers();
alreadyOpen = true;
}
}
public void close() { alreadyOpen = false; }
}
private class CloseNotifier extends Observable{
private boolean alreadyClosed = false;
public void notifyObservers() {
if(!isOpen && !alreadyClosed) {
setChanged();
super.notifyObservers();
alreadyClosed = true;
}
}
public void open() { alreadyClosed = false; }
}
}
class Bee {
private String name;
private OpenObserver openObsrv =
new OpenObserver();
private CloseObserver closeObsrv =
new CloseObserver();
public Bee(String nm) { name = nm; }
// An inner class for observing openings:
private class OpenObserver implements Observer{
public void update(Observable ob, Object a) {
System.out.println("Bee " + name
+ "'s breakfast time!");
}
}
// Another inner class for closings:
private class CloseObserver implements Observer{
public void update(Observable ob, Object a) {
System.out.println("Bee " + name
+ "'s bed time!");
}
}
public Observer openObserver() {
return openObsrv;
}
public Observer closeObserver() {
return closeObsrv;
}
}
class Hummingbird {
private String name;
private OpenObserver openObsrv =
new OpenObserver();
private CloseObserver closeObsrv =
new CloseObserver();
public Hummingbird(String nm) { name = nm; }
private class OpenObserver implements Observer{
public void update(Observable ob, Object a) {
System.out.println("Hummingbird " + name
+ "'s breakfast time!");
}
}
private class CloseObserver implements Observer{
public void update(Observable ob, Object a) {
System.out.println("Hummingbird " + name
+ "'s bed time!");
}
}
public Observer openObserver() {
return openObsrv;
}
public Observer closeObserver() {
return closeObsrv;
}
}
public class ObservedFlower extends TestCase {
Flower f = new Flower();
Bee
ba = new Bee("A"),
bb = new Bee("B");
Hummingbird
ha = new Hummingbird("A"),
hb = new Hummingbird("B");
public void test() {
f.opening().addObserver(ha.openObserver());
f.opening().addObserver(hb.openObserver());
f.opening().addObserver(ba.openObserver());
f.opening().addObserver(bb.openObserver());
f.closing().addObserver(ha.closeObserver());
f.closing().addObserver(hb.closeObserver());
f.closing().addObserver(ba.closeObserver());
f.closing().addObserver(bb.closeObserver());
// Hummingbird B decides to sleep in:
f.opening().deleteObserver(
hb.openObserver());
// A change that interests observers:
f.open();
f.open(); // It's already open, no change.
// Bee A doesn't want to go to bed:
f.closing().deleteObserver(
ba.closeObserver());
f.close();
f.close(); // It's already closed; no change
f.opening().deleteObservers();
f.open();
f.close();
}
public static void main(String args[]) {
junit.textui.TestRunner.run(ObservedFlower.class);
}
}
(观察者)感兴趣的事件是花朵的张开和关闭(open or close)。因为有内部类 (Inner class) 可以用,所以这两种事件可以被分开观察。OpenNotifier 和CloseNotifier都从Observable继承而来,它们都可以使用setChanged( )方法,都可以(作为Observable的子类)传递给需要一个Observable对象的地方。
使用inner class 来定义多个Observer也是非常方便的,对于Bee和Hummingbird(蜂鸟),它们可能需要相互独立的观察花朵的张开和关闭。请注意一下,通 过使用inner class这种方法,我们实现了一些通过继承才能得到的好处,而且更进一步,通过inner class你还可以访问外部类的私有数据成员,这是继承所不能做到的。
在Main( )函数里,你可以看到observer模式所带来的最大好处:通过向被观察者(Observables)动态的注册和卸载观察者对象(Observers),从而在运行时刻改变被观察者的行为。
研究一下上面那些代码你就会发现,OpenNotifier和CloseNotifier只用到了Observable类的基本接口。(译注:这里似乎作 者没写完,感觉不连贯)这就意味着你也可以继承其它完全不同的Observer类;观察者和花朵之间唯一的联系就是Observer接口。