【观察者模式】

43 阅读4分钟

定义

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己

以下是观察者模式的主要特点和定义:

主要角色

  1. Subject(主题)

    • 知道它的观察者。可以有任意多个观察者观察同一个主题。
    • 提供注册和删除观察者对象的接口。
    • 当主题的状态发生变化时,通知所有注册的观察者。
  2. Observer(观察者)

    • 为那些在主题发生变化时需要被通知的对象定义一个更新接口。
  3. ConcreteSubject(具体主题)

    • 将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
  4. ConcreteObserver(具体观察者)

    • 实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。

业务

假设我们有一个气象站,它可以测量温度、湿度和气压等数据。当气象数据发生变化时,需要通知多个显示设备,如显示屏、手机应用等。我们可以使用观察者模式来实现这个功能。

classDiagram
    class Observer {
        +update(float, float, float) : void
    }
    class Display {
        -float temperature
        -float humidity
        -float pressure
        +update(float, float, float) : void
        +display() : void
    }
    class Subject {
        +registerObserver(Observer) : void
        +removeObserver(Observer) : void
        +notifyObservers() : void
    }
    class WeatherData {
        -List<Observer> observers
        -float temperature
        -float humidity
        -float pressure
        +registerObserver(Observer) : void
        +removeObserver(Observer) : void
        +notifyObservers() : void
        +measurementsChanged() : void
        +setMeasurements(float, float, float) : void
    }
    Observer <|-- Display
    Subject <|-- WeatherData
    Observer  <-- WeatherData

一、定义观察者接口(Observer)

interface Observer {
    void update(float temperature, float humidity, float pressure);
}

二、定义主题接口(Subject)

interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

三、实现具体主题(ConcreteSubject)

class WeatherData implements Subject {
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        observers = new ArrayList<>();
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

四、实现具体观察者(ConcreteObserver)

class Display implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }

    private void display() {
        System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity and " + pressure + " pressure.");
    }
}

五、使用观察者模式

public class ObserverPatternExample {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        Display display = new Display();

        weatherData.registerObserver(display);

        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.9f);
        weatherData.removeObserver(display);
        weatherData.setMeasurements(78, 60, 31.1f);
    }
}

在这个例子中,WeatherData是具体主题,它实现了Subject接口,负责存储气象数据并通知观察者。Display是具体观察者,实现了Observer接口,负责接收气象数据并显示在屏幕上。通过这种方式,气象站可以将气象数据的变化通知多个显示设备,而不需要知道具体的显示设备是什么。这种设计实现了主题和观察者之间的松散耦合,使得系统更加灵活和易于维护

框架中的使用

各种消息队列

mq中的消费订阅

spring

一、事件驱动架构中的应用

Spring 的事件驱动架构基于观察者模式。可以定义事件(主题)和事件监听器(观察者)。当特定的事件发生时,Spring 会通知所有注册的监听器。

例如:

  1. 定义事件:
package com.example.events;

import org.springframework.context.ApplicationEvent;

public class CustomEvent extends ApplicationEvent {
    private String message;

    public CustomEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
  1. 定义事件监听器:
package com.example.events;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("Received custom event: " + event.getMessage());
    }
}
  1. 触发事件:
package com.example;

import com.example.events.CustomEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        context.publishEvent(new CustomEvent(Main.class, "Hello from custom event!"));
    }
}

在这个例子中,CustomEvent是自定义的事件,它继承自ApplicationEventCustomEventListener是事件监听器,实现了ApplicationListener接口。在main方法中,通过ApplicationContext触发事件,Spring 会自动通知所有注册的监听器。

总结

使用场景

  1. 当一个对象的状态变化需要通知其他对象,而又不希望它们之间有紧密的耦合关系时。

    • 例如在图形用户界面中,一个按钮的点击事件可能需要通知多个组件进行相应的更新。
  2. 当一个对象的变化会引起多个对象的变化时。

    • 比如在股票交易系统中,股票价格的变化会通知多个显示股票价格的界面进行更新。

优点

  1. 实现了主题和观察者之间的松散耦合。

    • 主题只需要知道有观察者存在,而不需要知道具体是哪些观察者。观察者只需要实现一个更新接口,而不需要了解主题的具体实现。
  2. 可以方便地添加和删除观察者。

    • 主题只需要通过注册和删除接口来管理观察者,而不需要修改其他代码。
  3. 支持广播通信。

    • 主题可以同时通知多个观察者,而不需要分别通知每个观察者。

缺点

  1. 如果一个主题有很多观察者,通知所有观察者可能会花费很多时间。
  2. 如果观察者和主题之间存在循环依赖,可能会导致系统死锁。
  3. 观察者模式可能会导致系统的复杂性增加,特别是当有很多观察者和主题时。