设计模式 --- 观察者模式

设计模式 --- 观察者模式

什么是观察者模式

  • 观察者模式(Observer Pattern)是一种行为型设计模式,用于在对象之间建立一对多的依赖关系。当一个对象(称为"主题"Subject)的状态发生改变时,所有依赖它的对象(称为"观察者"Observer)会自动收到通知并更新。这种模式的核心是解耦主题和观察者,让它们可以独立变化。
  • 核心思想
    主题(Subject):维护一个观察者列表,提供注册、注销和通知机制。
    观察者(Observer):定义统一的更新接口,当主题状态变化时被调用。
    松耦合:主题不需要知道观察者的具体实现,只需通过接口通知

观察者模式典型应用 --- C#中的事件

事件是什么?

  • 事件(Event)是 C# 中的一种语言机制 ,允许一个对象(发布者)在特定动作发生时,通知其他对象(订阅者)执行响应逻辑。
    它本质上是基于委托(Delegate)的封装,提供了一种安全、可控的观察者模型
  • 核心特点
  • 主题定义事件:声明事件并决定何时触发。
  • 观察者注册方法:将方法绑定到事件,事件触发时自动调用。
  • 解耦设计:主题和观察者无需直接依赖彼此的具体实现。

事件的原理

  • 底层机制:委托 + 观察者模式
  • 委托(Delegate)

    事件本质是一个多播委托MulticastDelegate),可以存储多个方法引用。当事件触发时,所有绑定的方法会被依次调用。

  • 观察者模式(Observer Pattern)

    • 发布者(Subject) :维护一个订阅者列表(通过委托链实现),提供订阅(+=)和取消订阅(-=)接口。
    • 订阅者(Observer):向发布者注册事件处理方法,事件触发时自动执行。

完整示例:温度监控系统

  • 场景描述
  • 发布者TemperatureMonitor 类,监控温度变化。
  • 订阅者
    • AlertSystem:温度过高时触发警报。
    • Logger:记录温度事件到日志。
  • 关键流程:
  • 定义事件 :使用 event 关键字声明一个委托类型的事件。
  • 订阅事件 :通过 += 将方法添加到事件的委托链。
  • 触发事件:在特定条件下调用事件,执行所有订阅的方法。

代码实现

csharp 复制代码
using System;

// 1. 定义事件参数(传递温度数据)
public class TemperatureEventArgs : EventArgs
{
    public double Temperature { get; }
    public TemperatureEventArgs(double temperature)
    {
        Temperature = temperature;
    }
}

// 2. 发布者(Subject)
public class TemperatureMonitor
{
    // 声明事件(基于泛型 EventHandler<T> 委托)
    // EventHandler<T> 是 .NET 内置的泛型委托,定义形式为:  
    // delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)
    public event EventHandler<TemperatureEventArgs> TemperatureExceeded;

    private double _currentTemperature;
    public double Threshold { get; set; } = 30.0;

    public void UpdateTemperature(double newTemperature)
    {
        _currentTemperature = newTemperature;
        Console.WriteLine($"当前温度: {_currentTemperature}°C");

        // 触发条件:温度超过阈值
        if (_currentTemperature > Threshold)
        {
            OnTemperatureExceeded(new TemperatureEventArgs(_currentTemperature));
        }
    }

    // 触发事件的方法(protected virtual 便于派生类扩展)
    protected virtual void OnTemperatureExceeded(TemperatureEventArgs e)
    {
        // 线程安全:临时保存事件引用,避免多线程下订阅者被移除导致的空引用
        var handler = TemperatureExceeded;
        handler?.Invoke(this, e); // 等同于 if (handler != null) handler(this, e);
    }
}

// 3. 订阅者(Observer)
public class AlertSystem
{
    public void HandleHighTemperature(object sender, TemperatureEventArgs e)
    {
        Console.WriteLine($"警报!温度过高: {e.Temperature}°C");
    }
}

public class Logger
{
    public void LogTemperatureEvent(object sender, TemperatureEventArgs e)
    {
        Console.WriteLine($"[日志] 温度事件: {e.Temperature}°C");
    }
}

// 4. 使用示例
class Program
{
    static void Main()
    {
        var monitor = new TemperatureMonitor();
        var alert = new AlertSystem();
        var logger = new Logger();

        // 订阅事件
        monitor.TemperatureExceeded += alert.HandleHighTemperature;
        monitor.TemperatureExceeded += logger.LogTemperatureEvent;

        // 模拟温度变化
        monitor.UpdateTemperature(25); // 不触发
        monitor.UpdateTemperature(32); // 触发事件
        monitor.UpdateTemperature(28); // 不触发
        monitor.UpdateTemperature(35); // 再次触发

        // 取消订阅
        monitor.TemperatureExceeded -= logger.LogTemperatureEvent;
        monitor.UpdateTemperature(40); // 仅触发警报
    }
}

输出结果

复制代码
当前温度: 25°C
当前温度: 32°C
警报!温度过高: 32°C
[日志] 温度事件: 32°C
当前温度: 28°C
当前温度: 35°C
警报!温度过高: 35°C
[日志] 温度事件: 35°C
当前温度: 40°C
警报!温度过高: 40°C
  • 观察者模式
  • TemperatureMonitor 是 Subject,维护订阅者列表(通过 TemperatureExceeded 事件的委托链)。
  • AlertSystemLogger` 是 Observers,通过订阅事件实现解耦。

总结

  • C# 中的事件是基于委托的观察者模式实现 ,通过 event 关键字提供安全的订阅机制。它广泛用于:
  • GUI 编程(如按钮点击)
  • 异步通知等场景,是解耦代码的关键工具

使用观察者模式实现事件处理机制

以下是一个使用 Java 手动实现事件处理机制的完整示例,基于观察者模式自定义接口,支持线程安全的订阅、取消订阅和事件触发功能。


事件对象(Event

java 复制代码
public class Event {
    private String message;
    private long timestamp;

    public Event(String message) {
        this.message = message;
        this.timestamp = System.currentTimeMillis();
    }

    public String getMessage() {
        return message;
    }

    public long getTimestamp() {
        return timestamp;
    }
}

事件监听器接口(EventListener

java 复制代码
@FunctionalInterface
public interface EventListener {
    void onEvent(Event event);
}

事件发布者(EventPublisher

java 复制代码
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.List;

public class EventPublisher {
    // 使用线程安全的集合存储监听器
    private final List<EventListener> listeners = new CopyOnWriteArrayList<>();

    // 订阅事件
    public void subscribe(EventListener listener) {
        listeners.add(listener);
        System.out.println("订阅成功: " + listener);
    }

    // 取消订阅
    public void unsubscribe(EventListener listener) {
        listeners.remove(listener);
        System.out.println("取消订阅: " + listener);
    }

    // 触发事件
    public void publishEvent(String message) {
        Event event = new Event(message);
        System.out.println("触发事件: " + event.getMessage());
        for (EventListener listener : listeners) {
            try {
                listener.onEvent(event);
            } catch (Exception e) {
                System.err.println("监听器处理异常: " + e.getMessage());
            }
        }
    }
}

定义具体监听器

java 复制代码
public class Main {
    public static void main(String[] args) {
        EventPublisher publisher = new EventPublisher();

        // 监听器1:Lambda表达式实现
        EventListener listener1 = event -> 
            System.out.printf("[Listener1] 收到事件: %s (时间: %d)%n",
                event.getMessage(), event.getTimestamp());

        // 监听器2:匿名类实现
        EventListener listener2 = new EventListener() {
            @Override
            public void onEvent(Event event) {
                System.out.printf("[Listener2] 处理事件: %s%n", event.getMessage());
            }
        };

        // 订阅
        publisher.subscribe(listener1);
        publisher.subscribe(listener2);

        // 触发事件
        publisher.publishEvent("Hello World!");

        // 取消订阅listener2后再次触发
        publisher.unsubscribe(listener2);
        publisher.publishEvent("Another Message");
    }
}

输出结果

复制代码
订阅成功: Main$$Lambda$1/0x0000000800b8a440@6d311334
订阅成功: Main$1@75bd9247
触发事件: Hello World!
[Listener1] 收到事件: Hello World! (时间: 1717500000000)
[Listener2] 处理事件: Hello World!
取消订阅: Main$1@75bd9247
触发事件: Another Message
[Listener1] 收到事件: Another Message (时间: 1717500001000)

关键设计解析

  • 3.1 线程安全
  • CopyOnWriteArrayList
    使用线程安全的集合存储监听器,确保在遍历过程中(如触发事件时)修改监听器列表不会导致 ConcurrentModificationException
  • 无锁设计
    添加/删除监听器时自动处理并发,无需显式加锁。
  • 观察者模式
  • Subject(主题)EventPublisher 维护监听器列表并提供订阅/取消订阅接口。
  • Observer(观察者)EventListener 接口的实现类,定义事件响应逻辑。

如果需要,可改为异步事件处理

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncEventPublisher extends EventPublisher {
    private final ExecutorService executor = Executors.newCachedThreadPool();

    @Override
    public void publishEvent(String message) {
        Event event = new Event(message);
        System.out.println("触发事件: " + event.getMessage());
        for (EventListener listener : listeners) {
            executor.submit(() -> {
                try {
                    listener.onEvent(event);
                } catch (Exception e) {
                    System.err.println("监听器处理异常: " + e.getMessage());
                }
            });
        }
    }
}
相关推荐
怡人蝶梦13 分钟前
Java后端技术栈问题排查实战:Spring Boot启动慢、Redis缓存击穿与Kafka消费堆积
java·jvm·redis·kafka·springboot·prometheus
瓯雅爱分享18 分钟前
MES管理系统:Java+Vue,含源码与文档,实现生产过程实时监控、调度与优化,提升制造企业效能
java·mysql·vue·软件工程·源代码管理
庄小焱1 小时前
设计模式——原型设计模式(创建型)
设计模式
鬼多不菜1 小时前
一篇学习CSS的笔记
java·前端·css
深色風信子1 小时前
Eclipse 插件开发 5.3 编辑器 监听输入
java·eclipse·编辑器·编辑器 监听输入·插件 监听输入
庄小焱1 小时前
设计模式——适配器设计模式(结构型)
设计模式
Blossom.1181 小时前
人工智能在智能健康监测中的创新应用与未来趋势
java·人工智能·深度学习·机器学习·语音识别
shangjg32 小时前
Kafka 如何保证不重复消费
java·分布式·后端·kafka
无处不在的海贼2 小时前
小明的Java面试奇遇之互联网保险系统架构与性能优化
java·面试·架构