观察者模式会在当观察对象的状态发生变化的时候,通知给观察者。适用于根据对象状态进行响应处理的场景。

示例程序:

观察者将观察一个会生成数值的对象,并将它的数值结果显示出来。不过,不同的观察者的显示方式不一样。

名字 说明
Observer 表示观察者的接口
NumberGenerator 表示生成数值的对象的抽象类
RandomNumberGenerator 生成随机数的类
DigitObserver 表示以数字形式显示数值的类
GraphObserver 表示以简单的图例形式显示数值的类
Maiin 测试程序行为的类

Observer接口:

Observer是“观察者”的接口,具体的观察者会实现这个接口。用于生产数值的NumberGenerator类会调用update方法,Generator有“生成器”“产生器”的意思。如果调用update方法,NumberGenerator类就会将“生成的数值发生,请更新显示内容”的通知发送给Observer。

  1. public interface Observer {
  2. public abstract void update(NumberGenerator generator);
  3. }

NumberGenerator类:

NumberGenerator类是用于生成数值的抽象类。生成数值的方法(Execute)和获取数值的方法(getNumber方法)都是抽象方法,需要子类去实现。
observers字段中保存有观察NumberGenerator的Observer们。
addObserver方法用于注册Observer,而deleteObserver方法用于删除Observer。notifyObservers方法会向所有Observer发送通知,告诉它们更新了,该方法会调用每个Observer的update方法。

  1. public abstract class NumberGenerator {
  2. private ArrayList observers = new ArrayList();
  3. public void addObserver(Observer observer) {
  4. observers.add(observer);
  5. }
  6. public void deleteObserver(Observer observer) {
  7. observers.remove(observer);
  8. }
  9. public void notifyObservers() {
  10. Iterator it = observers.iterator();
  11. while (it.hasNext()) {
  12. Observer next = (Observer) it.next();
  13. next.update(this);
  14. }
  15. }
  16. public abstract int getNumber(); // 获取数值
  17. public abstract void execute(); // 生成数值
  18. }

RandomNumberGenerator类:

是NumberGenerator的子类,它会生成随机数。Random字段中保存有java.util.Random类的实例(即随机数生成器)。而Number字段中保存的是当前生成的随机数。
execute方法会生成20个随机数(0-49的整数),并通过notifyObservers方法把每次生成结果通知给观察者。

  1. public class RandomNumberGenerator extends NumberGenerator {
  2. private Random random = new Random();
  3. private int number;
  4. @Override
  5. public int getNumber() {
  6. return number;
  7. }
  8. @Override
  9. public void execute() {
  10. for (int i = 0; i < 20; i++) {
  11. number = random.nextInt(50);
  12. notifyObservers();
  13. }
  14. }
  15. }

DigitObserver类:

实现了Observer接口。

  1. public class DigitObserver implements Observer{
  2. @Override
  3. public void update(NumberGenerator generator) {
  4. System.out.println("DigitObserver:"+generator.getNumber());
  5. try {
  6. Thread.sleep(100);
  7. } catch (InterruptedException e) {
  8. throw new RuntimeException(e);
  9. }
  10. }
  11. }

GraphObserver类:

实现了Observer接口。

  1. public class GraphObserver implements Observer{
  2. @Override
  3. public void update(NumberGenerator generator) {
  4. System.out.println("GraphObserver:");
  5. int count = generator.getNumber();
  6. for (int i = 0; i < count; i++) {
  7. System.out.print("*");
  8. }
  9. System.out.println("");
  10. try {
  11. Thread.sleep(100);
  12. } catch (InterruptedException e) {
  13. throw new RuntimeException(e);
  14. }
  15. }
  16. }

Main:

生成了一个RandomNumberGenerator类的实例和两个观察者。

  1. public class Main {
  2. public static void main(String[] args) {
  3. NumberGenerator generator = new RandomNumberGenerator();
  4. DigitObserver digitObserver = new DigitObserver();
  5. GraphObserver graphObserver = new GraphObserver();
  6. generator.addObserver(digitObserver);
  7. generator.addObserver(graphObserver);
  8. generator.execute();
  9. }
  10. }

Observer模式中的登场角色:

Subject(观察对象):

Subject角色表示观察对象,Subject角色中定义了注册观察者和删除观察者的方法。此外,它还声明了“获取现在的状态”的方法。

ConcreteSubject(具体的观察对象):

表示具体的被观察对象,。当自身状态发生变化后,会通知所有注册的Observer角色。

Observer(观察者):

Observer角色负责接收来自Subject角色的状态变化的通知,为此,它声明了update方法。

ConcreteObserver(具体的观察者):

角色表示具体的观察者,当它的update方法被调用后,会去获取要观察的对象的最新状态。


拓展思路的要点:

这里也出现了可替换性:

使用设计模式的目的之一就是使类称为可复用的组件。
在Observer模式中,有带状态的ConcreteSubject角色和接收状态变化通知的ConcreteObserver角色,连接这两个角色的就是它们的接口(API)Subject角色和Observer角色。
一方面RandomNumberGenerator类并不知道,也无需在意正在观察自己的到底是哪个具体实例。不过它知道在它的observers字段中保存的观察者们都实现了Observer接口。因为这些实例都是通过addObserver方法注册的,这就确保了它们一定都实现了Observer接口,一定会调用它们的update方法。
另一方面,DigitObserver类也无需在意自己观察的究竟是RandomNumberGenerator类的实例还是其它类的实例,只需要知道它们是NumberGenerator类的子类的实例,并持有getNumber方法。
如何实现可替换性的功能呢?

  • 利用抽象类和接口从具体类中抽出抽象方法;
  • 在将实例作为参数传递至类中,或者在类的字段中保存实例时,不适用具体类型,而是使用抽象类型和接口。

这样的实现方法可以帮助我们轻松替换具体类。

Observer的顺序:

Subject角色中注册有多个Observer角色。在实例程序中,先注册的Observer的update方法会先被调用。
通常,在设计ConcreteObserver角色的类时,需要注意这些Observer的update方法的调用顺序,不能因为update方法的调用顺序发生变化而产生问题。

从“观察”变为通知:

Observer本来的意思是“观察者”,但实际上Observer角色并非主动地去观察,而是被动地接受来自Subject角色的通知。因此,Observer模式也被称为Public-Subscibe(发布-订阅)模式。