参考文献

观察者模式

  • 定义对象之间的一对多依赖关系,以便当一个对象更改状态时,其所有依赖项都会收到通知并自动更新.
  • Model View Controller Pattern: 观察者模式用于模型视图控制器 (MVC) 架构模式.在 MVC 中,此模式用于将模型与视图解耦. View代表观察者,模型是Observable对象
  • Event management: 这是广泛使用观察者模式的领域之一. Swing 和.Net 广泛使用观察者模式来实现事件机制.

组件

  • Observable(Publisher): 定义将观察者附加到客户端和取消附加到客户端的操作的接口或抽象类.
  • ConcreteObservable: 具体的Observable类.它维护对象的状态,当状态发生变化时,它会通知附加的观察者.
  • Observer(Subscriber): 定义用于通知此对象的操作的接口或抽象类.
  • ConcreteObserver: 具体观察者实现

实现方式

  • 将业务逻辑拆分为发布者和订阅者两个部分

  • 声明订阅者接口Subscriber,该接口至少包含一个update方法

    1
    2
    3
    public interface Subscriber {
    void update(String message);
    }
  • 声明发布者接口Publisher.该接口定义了添加和删除订阅者的方法

    1
    2
    3
    4
    public interface Publisher {
    void addSubscriber(Subscriber subscriber);
    void removeSubscriber(Subscriber subscriber);
    }
  • 为存放实际订阅列表的位置创建一个抽象类AbstractPublisher,该类直接扩展自Publisher接口,并实现了订阅方法.具体的发布者可以继承这个抽象类来继承订阅行为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.util.ArrayList;
    import java.util.List;

    public abstract class AbstractPublisher implements Publisher {
    private List<Subscriber> subscribers = new ArrayList<>();

    public void addSubscriber(Subscriber subscriber) {
    subscribers.add(subscriber);
    }

    public void removeSubscriber(Subscriber subscriber) {
    subscribers.remove(subscriber);
    }

    protected void notifySubscribers(String message) {
    for (Subscriber subscriber : subscribers) {
    subscriber.update(message);
    }
    }
    }
  • 创建具体的发布者类.每当发布者发生重要事件时,需要通知所有的订阅者.我们可以通过调用notifySubscribers方法来完成通知操作

    1
    2
    3
    4
    5
    6
    7
    8
    public class ConcretePublisher extends AbstractPublisher {
    public void doSomethingImportant() {
    // 一些重要操作

    // 发布者发布事件,通知所有订阅者
    notifySubscribers("重要事件发生了!");
    }
    }
  • 在具体的订阅者类中实现update方法,用于接收通知并处理相关上下文数据

    1
    2
    3
    4
    5
    6
    public class ConcreteSubscriber implements Subscriber {
    public void update(String message) {
    System.out.println("收到通知:" + message);
    // 处理通知中的数据
    }
    }
  • 客户端需要生成所需的全部订阅者,并在相应的发布者处完成注册工作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Client {
    public static void main(String[] args) {
    // 创建发布者和订阅者
    Publisher publisher = new ConcretePublisher();
    Subscriber subscriber1 = new ConcreteSubscriber();
    Subscriber subscriber2 = new ConcreteSubscriber();

    // 注册订阅者
    publisher.addSubscriber(subscriber1);
    publisher.addSubscriber(subscriber2);

    // 发布者执行重要操作,会通知所有订阅者
    ((ConcretePublisher) publisher).doSomethingImportant();

    // 取消订阅
    publisher.removeSubscriber(subscriber1);

    // 发布者再次执行重要操作,只有一个订阅者会收到通知
    ((ConcretePublisher) publisher).doSomethingImportant();
    }
    }

使用场景

  • 当一个对象状态的改变需要改变其他对象, 或实际对象是事先未知的或动态变化的时, 可使用观察者模式.
  • 当应用中的一些对象必须观察其他对象时, 可使用该模式. 但仅能在有限时间内或特定情况下使用.

具体实施问题

  • Many subjects to Many observers

  • Who triggers the update?

  • Making sure Subject state is self-consistent before notification

  • Specifying points of interests

  • Encapsulating complex update semantics

Push and pull communication methods(推式和拉式通信方式)

  • Push model
    • 推送模型 - 主体向观察者发送有关更改的详细信息,无论观察者是否使用。由于主体需要向观察者发送详细信息,因此当需要发送大量数据但不使用这些数据时,这可能会效率低下。另一种方法是仅发送观察者所需的信息。在这种情况下,主体应该能够区分不同类型的观察者,并知道每个观察者所需的数据,这意味着主体层与观察者层的耦合程度更高。
  • Pull model
    • 拉模型 - 当主体的状态发生变化时,主体只是通知观察者,每个观察者都有责任从主体中提取所需的数据。这可能效率低下,因为通信分两步完成,并且在多线程环境中可能会出现问题。