跳到主要内容

观察者模式

  • 观察者模式(Observer Pattern):定义对象间一种一对多的依赖关系,使得每一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新
  • 观察者模式是一种对象行为型模式
  • 观察者模式的别名包括
    • 发布-订阅(Publish/Subscribe)模式
    • 模型-视图(Model/View)模式
    • 源-监听器(Source/Listener)模式
    • 从属者(Dependents)模式
  • 发布-订阅模式和观察者模式在细节上有些不同,可以理解成发布-订阅模式是广义上的观察者模式
    发布-订阅模式

结构

  • 角色
    UML
    • Subject(目标)
      被观察者,指被观察的对象,这个角色可以是接口,也可以是抽象类或者具体的类,因为大部分情况下会跟其他模式混用,因此使用抽象类的情况较多。
      • Vector 容器:类中有一个用于存放观察者对象的 Vector 容器(Vector 是线程安全的,因此不使用 ArrayList),这个容器是被观察者类的核心。
      • attach 方法:向容器中添加观察者对象。
      • detach 方法:从容器中移除观察者对象。
      • notify 方法:依次调用观察者对象的对应方法。
    • ConcreteSubject(具体目标)
      目标类的子类,通常包含经常发生改变的数据,当状态发生改变时。会向它的各个观察者发出通知。
      同时它还实现了目标类中定义的抽象业务逻辑方法(如果存在),如果无需扩展目标类,则具体目标类可以省略。
    • Observer(观察者)
      观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法 update(),因为又称为抽象观察者
    • ConcreteObserver(具体观察者)
      在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要跟具体目标的状态保持一致。
      它实现了在抽象观察者 Observer 中定义的 update() 方法。
      通常在实现时,可以调用具体目标类的 attach() 方法将自己添加到目标类的集合中,或者通过 detach() 方法将自己从目标类的集合中删除

实现

  • 代码示例

    // 1. 定义观察者接口  
    interface Observer {
    public void update();
    }

    // 2. 定义被观察者抽象类
    abstract class Subject {
    private Vector<Observer> obs = new Vector();

    public void addObserver(Observer obs){
    this.obs.add(obs);
    }
    public void delObserver(Observer obs){
    this.obs.remove(obs);
    }
    protected void notifyObserver(){
    for(Observer o: obs){
    o.update();
    }
    }
    public abstract void doSomething();
    }

    // 3. 具体的被观察者
    class ConcreteSubject extends Subject {
    public void doSomething(){
    System.out.println("被观察者事件发生改变");
    this.notifyObserver();
    }
    }

    // 4. 具体的观察者
    class ConcreteObserver1 implements Observer {
    public void update() {
    System.out.println("观察者1收到信息,并进行处理");
    }
    }
    class ConcreteObserver2 implements Observer {
    public void update() {
    System.out.println("观察者2收到信息,并进行处理");
    }
    }

    // 5. 客户端
    public class Client {
    public static void main(String[] args){
    Subject sub = new ConcreteSubject();
    sub.addObserver(new ConcreteObserver1()); //添加观察者1
    sub.addObserver(new ConcreteObserver2()); //添加观察者2
    sub.doSomething();
    }
    }

优缺点

  • 优点
    • 降低了目标和观察者之间的耦合关系,两者之间是抽象耦合关系
    • 目标和观察者之间建立了一套触发机制
    • 支持广播通信
    • 符合开闭原则
  • 缺点
    • 目标和观察者之间的依赖关系没有完全解除,而且可能出现循环引用
    • 当观察者对象很多时,发布通知会消耗大量时间,降低程序效率

应用

JDK 中的观察者模式

  • 在 JDK 的 java.util 包中提供了 Observable 类以及 Observer 接口,构成了 JDK 对观察者模式的支持,但在 Java9 被弃用

Spring 中的观察者模式

  • Spring 中的事件驱动模型就是经典的观察者模式应用,也就是项目中最常见的事件监听器
  • Spring 中观察者模式的四个角色
    1. 事件ApplicationEvent 是所有事件对象的父类,继承自 JDK 中的 EventObject,所有的事件都需要继承 ApplicationEvent 并且通过 source 得到事件源
      Spring 中的内置事件ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEventRequestHandledEvent
    2. 事件监听ApplicationListener,也就是观察者,继承自 JDK 中的 EventListener,该类中只有一个方法:onApplicationEvent,当监听的事件发生后该方法会被执行
    3. 事件源ApplicationContext 是 Spring 中的核心容器,在事件中 ApplicationContext 可以作为事件的发布者(即事件源),因为 ApplicationContext 继承自 ApplicationEventPublisher,ApplicationEventPublisher 中定义了事件发布的方法:publishEvent(Object object)
    4. 事件管理ApplicationEventMulticaster,用于事件监听器的注册和事件的广播(将 ApplicationContext 发布的 Event 广播给它的监听器列表)