观察者模式 - Observer Pattern, 也叫做发布订阅模式(Publish / subscribe)

WHAT

定义

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

类图

Subject 被观察者对象

定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。

一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。

public abstract class Subject {
  // 定义一个观察者数组
  private Vector<Observer> obsVector = new Vector<Observer>();
  // 增加一个观察者
  public void addObserver(Observer o){
    this.obsVector.add(o);
  }
  // 删除一个观察者
  public void delObserver(Observer o){
    this.obsVector.remove(o);
  }
  // 通知所有观察者
  public void notifyObservers(){
    for(Observer o: this.obsVector){
      o.update();
    }
  }
}

Tips:

程序中使用ArrayList和Vector没有太大的差别,ArrayList是非线程安全的,Vector是线程安全的。

Observer 观察者对象

观察者接收到消息后,即进行update(更新方法)操作,对接受到的信息进行处理

public interface Observer{
  // 更新方法
  public void update();
  
}

观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。

ConcreteSubject 具体的被观察者

定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知

public class ConcreteSubject extends Subject{
  //具体的业务
  public void doSomething(){
    /*
     * do something
     */
    super.notifyObservers();
  }
}

这是最简单的被观察者实现,只通知了观察者的逻辑,具体项目中会加入需要的逻辑。

ConcreteObserver 具体的观察者

实现观察者自己的逻辑。

public class ConcreteObserver implements Observer{
  // 实现更新方法
  public void update(){
    System.out.println("接收到信息,并进行处理!");
  }
}

场景类

public class Client{
  public static void main(String[] args){
    // 创建一个被观察者
    ConcreteSubject subject = new ConcreteSubject();
    // 定义一个观察者
    Observer obs = new ConcreteObserver();
    // 观察者观察被观察者
    subject.addObserver(obs);
    // 观察者开始活动了
		subject.doSomething();
  }
}

WHY

优势

  • 观察者和被观察者之间是抽象耦合
    • 不管是增加观察者还是被观察者都非常容易扩展
  • 建立一套触发机制
    • 每一个类的职责都是单一的,观察者模式可以通过动作,将单一职责的类都串联在一起

劣势

  • 开发效率和运行效率
    • 一个被观察者,多个观察者,开发和调试会比较复杂
    • 在单一线程中,Java默认是顺序执行,一个观察者卡壳,会影响整体的效率

HOW

使用场景

  • 关联行为场景
    • 是可拆分的关联行为,不是组合型
  • 时间多级触发场景
  • 跨系统的消息交换场景
    • 消息队列的处理机制

注意事项

广播链的问题

如果一个观察者有了双重身份,既是观察者,也是被观察者,当链一旦建立,A触发B,B触发C,整个逻辑就比较复杂,且可维护性差。

与责任链模式的区别

观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构

责任链模式在消息传递过程中基本上保持消息不变,如果要改变,也只是在原有的消息上进行修正。

异步处理问题

观察者较多,且处理时间长时,会使用异步处理。

异步处理就要考虑线程安全和队列的问题。

MessageQueue

项目中真实的观察者模式

  • 观察者和被观察者之间的消息沟通
    • 被观察者状态改变会触发观察者的一个行为,同时会传递一个消息给观察者
    • 实际中:观察者中的update方法接受两个参数,一个是被观察者,一个是DTO(Data Transfer Object),由被观察者生成,由观察者消费
  • 观察者响应方式
    • 多线程技术,无论是被观察者启动线程还是观察者启动线程,都可以明显地提高系统性能,通常所说的异步架构
    • 缓存技术,准备足够的资源,保证快速响应,代价是开发难度大,而且压力测试要做足够充分,通常所说的同步架构
  • 被观察者尽量自己做主
    • 被观察者的状态改变不一定必须通知观察者,所以在设计时要灵活考虑,否则会加重观察者的处理逻辑
    • 一般是,对被观察者的业务逻辑doSomething方法实现重载,如增加一个doSomething(boolean notify)方法,决定是否通知观察者,而不是消息到达观察者时才判断是否要消费。

TODO

Spring中的消息订阅机制


《设计模式之禅》 第22章 观察者模式