【设计模式】【行为型模式】观察者模式(Observer)

简介: 一、入门 什么是观察者模式? 观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是观察者模式?

观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。

为什么要观察者模式?

假设我们正在开发一个天气预报系统,其中:

  • WeatherStation(气象站):负责收集天气数据(如温度、湿度等)。
  • Display(显示设备):负责显示天气数据,比如手机App、电子屏等。

当气象站的数据更新时,所有显示设备都需要实时更新显示内容。
下面是没有观察者模式时的实现:

class WeatherStation {
   
    private float temperature;
    private float humidity;
    private PhoneDisplay phoneDisplay;
    private TVDisplay tvDisplay;

    public void setPhoneDisplay(PhoneDisplay phoneDisplay) {
   
        this.phoneDisplay = phoneDisplay;
    }

    public void setTVDisplay(TVDisplay tvDisplay) {
   
        this.tvDisplay = tvDisplay;
    }

    public void removePhoneDisplay() {
   
        phoneDisplay = null;
    }

    public void removeTVDisplay() {
   
        phoneDisplay = null;
    }

    public void setMeasurements(float temperature, float humidity) {
   
        this.temperature = temperature;
        this.humidity = humidity;
        // 手动调用显示设备的更新方法
        if (phoneDisplay != null) {
   
            phoneDisplay.update(temperature, humidity);
        }
        if (tvDisplay != null) {
   
            tvDisplay.update(temperature, humidity);
        }
    }
}

紧耦合
如果没有观察者模式,气象站需要直接知道所有显示设备的存在,并手动调用它们的更新方法。例如:
问题:

  • 气象站和显示设备之间是紧耦合的,气象站需要知道所有显示设备的具体实现。
  • 如果新增一个显示设备(比如智能手表),需要修改气象站的代码,违反了开闭原则(对扩展开放,对修改关闭)。

难以动态管理依赖
如果显示设备需要动态添加或移除(比如用户关闭了某个显示设备),气象站需要手动管理这些设备的引用。

扩展性差
如果未来需要支持更多类型的观察者(比如日志记录器、报警系统等),气象站的代码会变得越来越臃肿,难以维护。

怎么实现观察者模式?

在观察者模式中有如下角色:

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

【案例】天气站 - 改

image.png

Observer观察者: Observer接口

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

Subject主题: subject接口

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

实现具体主题(气象站): WeatherStation

class WeatherStation implements Subject {
   
    private List<Observer> observers = new ArrayList<>();
    private float temperature;
    private float humidity;

    @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);
        }
    }

    public void setMeasurements(float temperature, float humidity) {
   
        this.temperature = temperature;
        this.humidity = humidity;
        notifyObservers(); // 通知所有观察者
    }
}

实现具体观察者(显示设备): PhoneDisplay类和TVDisplay

class PhoneDisplay implements Observer {
   
    @Override
    public void update(float temperature, float humidity) {
   
        System.out.println("手机显示:温度 = " + temperature + ",湿度 = " + humidity);
    }
}

class TVDisplay implements Observer {
   
    @Override
    public void update(float temperature, float humidity) {
   
        System.out.println("电视显示:温度 = " + temperature + ",湿度 = " + humidity);
    }
}

测试类

public class WeatherApp {
   
    public static void main(String[] args) {
   
        WeatherStation weatherStation = new WeatherStation();

        Observer phoneDisplay = new PhoneDisplay();
        Observer tvDisplay = new TVDisplay();

        weatherStation.registerObserver(phoneDisplay);
        weatherStation.registerObserver(tvDisplay);

        // 更新天气数据
        weatherStation.setMeasurements(25.5f, 60.0f);

        // 移除一个观察者
        weatherStation.removeObserver(tvDisplay);

        // 再次更新天气数据
        weatherStation.setMeasurements(26.0f, 58.0f);
    }
}

运行结果

手机显示:温度 = 25.5,湿度 = 60.0
电视显示:温度 = 25.5,湿度 = 60.0
手机显示:温度 = 26.0,湿度 = 58.0

二、观察者模式在源码中运用

Java 中的 java.util.Observer 和 java.util.Observable

Java 标准库中提供了观察者模式的实现,分别是 Observer 接口和 Observable 类。

  • Observable 是被观察者的基类,内部维护了一个观察者列表,并提供了 addObserver、deleteObserver 和 notifyObservers 方法。
  • Observer 是观察者接口,定义了 update 方法,用于接收通知。

Observer和Observable的使用

被观察者(具体主题):WeatherData

// 被观察者(主题)
class WeatherData extends Observable {
   
    private float temperature;
    private float humidity;

    public void setMeasurements(float temperature, float humidity) {
   
        this.temperature = temperature;
        this.humidity = humidity;
        setChanged(); // 标记状态已改变
        notifyObservers(); // 通知观察者
    }

    public float getTemperature() {
   
        return temperature;
    }

    public float getHumidity() {
   
        return humidity;
    }
}

观察者(具体观察者): Display

// 观察者
class Display implements Observer {
   
    @Override
    public void update(Observable o, Object arg) {
   
        if (o instanceof WeatherData) {
   
            WeatherData weatherData = (WeatherData) o;
            float temperature = weatherData.getTemperature();
            float humidity = weatherData.getHumidity();
            System.out.println("当前温度: " + temperature + ",湿度: " + humidity);
        }
    }
}

测试

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

        weatherData.addObserver(display); // 注册观察者

        weatherData.setMeasurements(25.5f, 60.0f); // 更新数据并通知观察者
    }
}

输出结果

当前温度: 25.5,湿度: 60.0

Observer和Observable的源码实现

观察者:Observer类,入参Observable o:被观察的对象(主题)和 Object arg:传递给观察者的额外参数(可选)。

public interface Observer {
   
    void update(Observable o, Object arg);
}

主题Observable类。
我们可以看到Vector<Observer>存储观察者列表。并且因为加了synchronized关键字,这些方法都是线程安全的。notifyObservers方法会遍历观察者列表,并调用每个观察者的update方法。

public class Observable {
   
    // 标记对象是否已改变
    private boolean changed = false;

    // 观察者列表(使用 Vector 保证线程安全)
    private Vector<Observer> obs;

    public Observable() {
   
        obs = new Vector<>();
    }

    // 添加观察者
    public synchronized void addObserver(Observer o) {
   
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
   
            obs.addElement(o);
        }
    }

    // 删除观察者
    public synchronized void deleteObserver(Observer o) {
   
        obs.removeElement(o);
    }

    // 通知所有观察者(无参数)
    public void notifyObservers() {
   
        notifyObservers(null);
    }

    // 通知所有观察者(带参数)
    public void notifyObservers(Object arg) {
   
        Observer[] arrLocal;

        // 同步块,确保线程安全
        synchronized (this) {
   
            if (!changed) // 如果没有变化,直接返回
                return;
            arrLocal = obs.toArray(new Observer[obs.size()]);
            clearChanged(); // 重置变化标志
        }

        // 遍历观察者列表,调用 update 方法
        for (Observer observer : arrLocal) {
   
            observer.update(this, arg);
        }
    }

    // 删除所有观察者
    public synchronized void deleteObservers() {
   
        obs.removeAllElements();
    }

    // 标记对象已改变
    protected synchronized void setChanged() {
   
        changed = true;
    }

    // 重置变化标志
    protected synchronized void clearChanged() {
   
        changed = false;
    }

    // 检查对象是否已改变
    public synchronized boolean hasChanged() {
   
        return changed;
    }

    // 返回观察者数量
    public synchronized int countObservers() {
   
        return obs.size();
    }
}

Spring 框架中的事件机制

Spring 框架中的事件机制是基于观察者模式实现的。它允许开发者定义自定义事件,并通过监听器(观察者)来处理这些事件。

Spring事件机制的使用

自定义事件

class CustomEvent extends ApplicationEvent {
   
    private String message;

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

    public String getMessage() {
   
        return message;
    }
}

事件监听器(观察者)

@Component
class CustomEventListener implements ApplicationListener<CustomEvent> {
   
    @Override
    public void onApplicationEvent(CustomEvent event) {
   
        System.out.println("收到事件: " + event.getMessage());
    }
}

事件发布者

@Component
class CustomEventPublisher {
   
    private final AnnotationConfigApplicationContext context;

    public CustomEventPublisher(AnnotationConfigApplicationContext context) {
   
        this.context = context;
    }

    public void publishEvent(String message) {
   
        context.publishEvent(new CustomEvent(this, message));
    }
}

配置类

@Configuration
@ComponentScan
class AppConfig {
   }

测试类

public class SpringEventDemo {
   
    public static void main(String[] args) {
   
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        CustomEventPublisher publisher = context.getBean(CustomEventPublisher.class);
        publisher.publishEvent("Hello, Spring Event!");
        context.close();
    }
}

输出内容

收到事件: Hello, Spring Event!

Spring的事件机质的源码实现

Spring 事件机制的核心组件包括:

  • ApplicationEvent:事件的基类,所有自定义事件都需要继承它。对应观察者模式中的“事件”。
  • ApplicationListener:观察者接口,定义了处理事件的方法。对应观察者模式中的“观察者”。
  • ApplicationEventPublisher:事件发布者接口,用于发布事件。对应观察者模式中的“主题”。
  • ApplicationEventMulticaster:事件广播器,负责将事件分发给所有监听器。类似于观察者模式中的“通知机制”。

ApplicationEventApplicationEvent 是所有事件的基类,它继承自 java.util.EventObject

public abstract class ApplicationEvent extends EventObject {
   
    private final long timestamp;            // timestamp: 事件发生的时间戳。

    public ApplicationEvent(Object source) {
    // source:事件源,通常是发布事件的对象。
        super(source);
        this.timestamp = System.currentTimeMillis();
    }

    public final long getTimestamp() {
   
        return this.timestamp;
    }
}

ApplicationListener接口: ApplicationListener是观察者接口,定义了处理事件的方法。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
   
    void onApplicationEvent(E event);  // 当事件发生时,会调用此方法。
}

ApplicationEventPublisher接口: 是事件发布者接口,用于发布事件。

@FunctionalInterface
public interface ApplicationEventPublisher {
   
    default void publishEvent(ApplicationEvent event) {
   
        publishEvent((Object) event);
    }

    void publishEvent(Object event);
}

ApplicationEventMulticaster接口:是事件广播器接口,负责将事件分发给所有监听器。

public interface ApplicationEventMulticaster {
   
    void addApplicationListener(ApplicationListener<?> listener);
    void addApplicationListenerBean(String listenerBeanName);
    void removeApplicationListener(ApplicationListener<?> listener);
    void removeApplicationListenerBean(String listenerBeanName);
    void removeAllListeners();
    void multicastEvent(ApplicationEvent event); 
    void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}

SimpleApplicationEventMulticasterSimpleApplicationEventMulticasterApplicationEventMulticaster的默认实现类。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
   
    // 遍历所有监听器,并调用 onApplicationEvent 方法。
    @Override
    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
   
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
   
            Executor executor = getTaskExecutor();
            if (executor != null) {
   
                executor.execute(() -> invokeListener(listener, event));
            } else {
   
                invokeListener(listener, event);
            }
        }
    }

    // 实际调用监听器的 onApplicationEvent 方法。
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
   
        try {
   
            listener.onApplicationEvent(event);
        } catch (ClassCastException ex) {
   
            // 处理类型转换异常
        }
    }
}

三、总结

观察者模式的优点

解耦:主题(Subject)和观察者(Observer)之间是松耦合的(主题不需要知道观察者的具体实现,只需要知道观察者接口,观察者也不需要知道主题的具体实现,只需要实现观察者接口)
动态管理依赖:观察者可以动态注册和注销,而不需要修改主题的代码。支持运行时动态添加或移除观察者,灵活性高。

符合开闭原则:可以轻松添加新的观察者,而不需要修改主题的代码。主题的代码不需要因为观察者的变化而修改。

广播通信:主题可以一次性通知所有观察者,适合一对多的通信场景。观察者可以根据需要选择是否响应通知。

职责分离:主题负责维护状态和通知观察者。观察者负责处理状态变化的逻辑。职责分离使得代码更加清晰和可维护。

观察者模式的缺点

  • 性能问题
    • 如果观察者数量非常多,通知所有观察者可能会消耗大量时间。
    • 如果观察者的处理逻辑复杂,可能会导致性能瓶颈。
  • 内存泄漏
    • 如果观察者没有正确注销,可能会导致观察者无法被垃圾回收,从而引发内存泄漏。
    • 特别是在长时间运行的应用中,需要特别注意观察者的生命周期管理。
  • 调试困难
    • 由于观察者和主题是松耦合的,调试时可能难以追踪事件的传递路径。
    • 如果观察者的处理逻辑出现问题,可能不容易定位问题根源。
  • 事件顺序不确定
    • 观察者收到通知的顺序通常是不确定的,如果业务逻辑对顺序有要求,可能需要额外的处理。

观察者模式的适用场景

  • 事件驱动系统
    • 当一个对象的状态变化需要触发其他对象的操作时,可以使用观察者模式。
    • 例如:GUI 框架中的按钮点击事件、Spring 框架中的事件机制。
  • 一对多的依赖关系
    • 当一个对象的状态变化需要通知多个其他对象时,可以使用观察者模式。
    • 例如:气象站和多个显示设备的关系。
  • 跨系统的消息通知
    • 在分布式系统中,观察者模式可以用于实现消息的发布和订阅。
    • 例如:消息队列(MQ)中的发布-订阅模型。
  • 状态变化的广播
    • 当一个对象的状态变化需要广播给多个对象时,可以使用观察者模式。
    • 例如:游戏中的角色状态变化通知其他系统(如 UI、音效等)。
  • 解耦业务逻辑
    • 当需要将业务逻辑解耦为多个独立的模块时,可以使用观察者模式。
    • 例如:订单系统中的订单状态变化通知库存系统、物流系统等。

参考

黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili

目录
相关文章
|
15天前
|
设计模式 网络协议 Java
【设计模式】【行为型模式】状态模式(State)
一、入门 什么是状态模式? 状态模式(State Pattern)是一种行为设计模式,允许对象在其内部状态改变时改变其行为,使其看起来像是改变了类。状态模式的核心思想是将对象的状态封装成独立的类,并将
59 16
|
15天前
|
设计模式 算法 前端开发
【设计模式】【行为型模式】职责链模式(Chain of Responsibility)
一、入门 什么是职责链模式? 职责链模式是一种行为设计模式,它允许你将请求沿着一条链传递,直到有对象处理它为止。每个对象都有机会处理请求,或者将其传递给链中的下一个对象。 为什么需要职责链模式? 使用
55 16
|
12天前
|
设计模式 存储 Java
【设计模式】【行为型模式】备忘录模式(Memento)
一、入门 什么是备忘录模式? 备忘录模式(Memento Pattern)是一种行为设计模式,用于在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在需要时恢复该状态。它通常用于实现撤销操作
54 8
|
15天前
|
设计模式 消息中间件 Java
【设计模式】【行为型模式】命令模式(Command)
一、入门 什么是命令模式? 命令模式是一种行为设计模式,它将请求或操作封装为对象,从而使你可以用不同的请求对客户进行参数化,并支持请求的排队、记录、撤销等操作。 命令模式的核心是将“请求”封装为独立的
63 15
|
15天前
|
设计模式 算法 搜索推荐
【设计模式】【行为型模式】策略模式(Strategy)
一、入门 什么是策略模式? 策略模式是一种行为设计模式,允许在运行时选择算法或行为。它将算法封装在独立的类中,使得它们可以互换,而不影响客户端代码。 为什么需要策略模式? 策略模式的主要目的是解决算法
47 14
|
12天前
|
设计模式 Java 编译器
【设计模式】【行为型模式】解释器模式(Interpreter)
一、入门 什么是解释器模式? 解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义语言的语法表示,并提供一个解释器来处理该语法。它通常用于需要解释和执行特定语言或表达式的场
48 11
|
12天前
|
设计模式 存储 JavaScript
【设计模式】【行为型模式】迭代器模式(Iterator)
一、入门 什么是迭代器模式? 迭代器模式(Iterator Pattern)是一种行为设计模式,它提供了一种顺序访问聚合对象中元素的方法,而不需要暴露其底层表示。迭代器模式将遍历逻辑从聚合对象中分离出
45 11
|
15天前
|
设计模式 数据采集 算法
【设计模式】【行为型模式】模板方法模式(Template Method)
一、入门 1.1、什么是模板方法模式? 模板模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的框架,并允许子类在不改变算法结构的情况下重新定义算法的某些步骤。
42 13
|
12天前
|
设计模式 XML JSON
【设计模式】【行为型模式】访问者模式(Visitor)
一、入门 什么是访问者模式? 访问者模式(Visitor Pattern)是一种行为设计模式,允许你将算法与对象结构分离。通过这种方式,可以在不改变对象结构的情况下,向对象结构中的元素添加新的操作。
47 10
|
12天前
|
设计模式 Java 程序员
【设计模式】【行为型模式】中介者模式(Mediator)
一、入门 什么是中介者模式? 中介者模式(Mediator Pattern)是一种行为设计模式,旨在减少对象之间的直接依赖,通过引入一个中介者对象来协调多个对象之间的交互。这种模式特别适用于对象间存在
42 9

热门文章

最新文章

OSZAR »