观察者模式
也称为发布-订阅模式。Observer模式适用于根据对象状态进行相应处理的场景。
示例程序
观察者将观察一个会生成数值的对象,并将它生成的数值结果显示出来。不过,不同的观察者的显示方式不一样。DigitObserver会以数字的形式显示数值,而GraphObserver则会以“*”的形式显示数值。
观察者接口 Observer
public interface Observer {
public abstract void update(NumberGenerator generator);
}
观察对象 NumberGenerator,用于生成数值的抽象类,里面保存了各种观察者observers,有增加和删除观察者的方法addObserver和deleteObserver,以及向所有观察者发送通知的方法notifyObservers。生成数值的方法execute和获取数值的方法getNumber是抽象方法,交由具体的观察对象去实现。
public abstract class NumberGenerator {
private ArrayList observers = new ArrayList();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void deleteObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
Iterator it = observers.iterator();
while (it.hasNext()) {
Observer o = (Observer) it.next();
o.update(this);
}
}
public abstract int getNumber();
public abstract void execute();
}
具体的观察对象 RandomNumberGenerator,它会生成随机数
public class RandomNumberGenerator extends NumberGenerator{
private Random random = new Random();
public int number;
@Override
public int getNumber() {
return number;
}
@Override
public void execute() {
for (int i = 0; i < 20; i++) {
number = random.nextInt(50);
notifyObservers();
}
}
}
具体的观察者 DigitObserver,实现了Observer接口,update方法是将传过来的观察对象,输出其生成的数值。(为了方便观察,使用Thread.sleep降低了程序的运行速度)
public class DigitObserver implements Observer {
@Override
public void update(NumberGenerator generator) {
System.out.println("DigitObserver:" + generator.getNumber());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
具体的观察者 GraphObserver, 同样实现了Observer接口,update方法将观察对象生成的数值以***的形式显示。
public class GraphObserver implements Observer {
@Override
public void update(NumberGenerator generator) {
System.out.print("GraphObserver:");
for (int i = 0; i < generator.getNumber(); i++) {
System.out.print("*");
}
System.out.println("");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类
public class Main {
public static void main(String[] args) {
NumberGenerator generator = new RandomNumberGenerator();
Observer observer1 = new DigitObserver();
Observer observer2 = new GraphObserver();
generator.addObserver(observer1);
generator.addObserver(observer2);
generator.execute();
}
}
输出结果
注意的点
- 尽量不要在Observer中对观察对象进行修改操作,否则容易引发循环调用(即subject状态改变,通知observer,observer修改subject的状态,导致subject又触发通知observer,无限循环)。
- 为了保持各个Observer类的独立性,不能因为update方法的调用顺序发生改变而产生问题。
- 传递更新信息的方式,有三种,1是直接将观察对象作为update方法的参数给到observer;2是将观察对象和更新值同时作为参数;3是只传递更新值(这样subject里的依赖性很强,不好)
- 实际上Observer角色并非主动地去观察,而是被动地接受来自subject角色的通知,因此Observer模式也被称为Publish-Subscribe(发布-订阅)模式。
java.util.Observer接口
其实java类库中的java.util.Observer接口和java.util.Observable类提供了实现Observer模式的工具类,只是由于观察对象都需要继承Observable这个抽象类才可作为subject角色。但是java是单继承的,原先的观察对象可能已经是其他类的子类了,就没办法用这套工具类来实现Observer模式,所以被弃用了。