在Observer模式中,当观察对象的状态发生变化时,会通知给观察者。Observer模式适用于根据对象状态进行相应处理的场景。
简单一句话概况就是:观察者会发送观察对象状态变化的通知。
下面示例程序中,observer将观察一个会生成数值的对象,并将它生成的数值结果显示出来。不过,不同的观察者的显示方式不一样。Digitobserver会以数字形式显示数值,而Graphobserver则会以简单的图示形式来显示数值。
先看一下所有的类和接口:
类图:
Observer接口是表示“观察者”的接口。具体的观察者会实现这个接口。
用于生成数值的NumberGenerator类会调用update方法,将“生成的数值发生了变化,请更新显示内容”的通知发送给Observer。
public interface Observer {
public abstract void update(NumberGenerator generator);
}
NumberGenerator类(代码清单17-2)是用于生成数值的抽象类。生成数值的方法( execute方法)和获取数值的方法( getNumber方法)都是抽象方法,需要子类去实现。
addObserver方法用于注册observer,而deleteobserver方法用于删除observer。
notifyObservers方法会向所有的observer发送通知,告诉它们“我生成的数值发生了变化,请更新显示内容”。该方法会调用每个observer的update方法。
public abstract class NumberGenerator {
//用于保存所有的observer
private ArrayList observers = new ArrayList();
//注册observer
public void addObserver(Observer observer) {
observers.add(observer);
}
//删除observer
public void deleteObserver(Observer observer) {
observers.remove(observer);
}
//向observer发送通知
public void notifyObservers() {
//把每个observer拿出来,调用他的update方法
Iterator it = observers.iterator();
while (it.hasNext()) {
Observer o = (Observer) it.next();
o.update(this);
}
}
//获取数值
public abstract int getNumber();
//生成数值
public abstract void execute();
}
RandomNumberGenerator类是NumberGenerator的子类,它会生成随机数。
getNumber方法用于获取number字段的值。execute方法会生成20个0 ~49的随机整数,并通过notifyObservers方法把每次生成结果通知给观察者。
public class RandomNumberGenerator extends NumberGenerator{
//生成随机数
private Random random = new Random();
//当前数值
private 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方法接收NumberGenerator的实例作为参数,然后通过调用NumberGenerator类的实例的getNumber方法可以获取到当前的数值,并将这个数值显示出来。
public class DigitObserver implements Observer{
@Override
public void update(NumberGenerator generator) {
System.out.println("DigitObserver:" + generator.getNumber());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
GraphObserver类也实现了Observer接口。
该类会将观察到的数值以*****这样的简单图示的形式显示出来。
public class GraphObserver implements Observer{
@Override
public void update(NumberGenerator generator) {
System.out.println("GraphObserver:");
int count = generator.getNumber();
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println("");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
Main类生成了一个RandomNumberGenerator类的实例和两个观察者,其中observer1是 DigitObserver类的实例,observer2是GraphObserver类的实例。
在使用addObserver注册观察者后,它还会调用generator.execute方法生成随机数值。
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();
}
}
部分运行结果
DigitObserver:21
GraphObserver:
*********************
DigitObserver:7
GraphObserver:
*******
DigitObserver:31
GraphObserver:
*******************************
DigitObserver:24
GraphObserver:
************************
使用设计模式的目的之一就是使类成为可复用的组件。
在Observer模式中,有带状态的ConcreteSubject角色和接收状态变化通知的ConcreteObserver角色。连接这两个角色的就是它们的接口(API ) Subject角色和 Observer 角色。
一方面RandomNumberGenerator类并不知道,也无需在意正在观察自己的(自己需要通知的对象)到底是DigitObserver类的实例还是GraphObserver类的实例。不过它知道在它的observers字段中所保存的观察者们都实现了observer接口,一定可以调用它们的update方法。
另一方面,DigitObserver类也无需在意自己正在观察的究竟是RandomNumberGenerator类的实例还是其他XxxxNumberGenerator类的实例。不过,Digitobserver类知道它们是NumberGenerator类的子类的实例,并持有getNumber方法。
我们应该注意到这种可替换性的思想:
利用抽象类和接口从具体类中抽出抽象方法
在将实例作为参数传递至类中,或者在类的字段中保存实例时,不使用具体类型,而是使用抽象类型和接口
这样的实现方式可以帮助我们轻松替换具体类。
Observer本来的意思是“观察者”,但实际上Observer角色并非主动地去观察,而是被动地接受来自Subject角色的通知。因此,Observer模式也被称为Publish-Subscribe(发布-订阅)模式。个人认为Publish(发布)和 Subscribe(订阅)这个名字可能更加合适。
在Mediator模式中,有时会使用Observer模式来实现Mediator角色与Colleague角色之间的通信。
就“发送状态变化通知”这一点而言,Mediator模式与Observer模式是类似的。不过,两种模式中,通知的目的和视角不同。
在 Mediator模式中,虽然也会发送通知,不过那不过是为了对Colleague角色进行仲裁而已。
而在Observer模式中,将Subject角色的状态变化通知给Observer角色的目的则主要是为了使Subject角色和 Observer角色同步。