委托、事件、发布-订阅模式全梳理(完整总结)

目录

  • [Java 实现基于委托+事件思想的发布-订阅(标准版)](#Java 实现基于委托+事件思想的发布-订阅(标准版))
    • 场景
      • [1. 订单事件对象(继承EventObject)](#1. 订单事件对象(继承EventObject))
      • [2. 事件监听器接口(模拟委托,继承EventListener)](#2. 事件监听器接口(模拟委托,继承EventListener))
      • [3. 发布者(订单服务)](#3. 发布者(订单服务))
      • [4. 订阅者实现](#4. 订阅者实现)
      • [5. 测试主类](#5. 测试主类)
      • 运行结果
      • C#与Java关键对应
      • 核心设计要点
      • 拓展方向
  • [Java 纯手动实现(无继承EventObject/EventListener)](#Java 纯手动实现(无继承EventObject/EventListener))
    • [1. 纯事件数据类(无继承)](#1. 纯事件数据类(无继承))
    • [2. 纯函数式监听器接口(无继承)](#2. 纯函数式监听器接口(无继承))
    • [3. 发布者(逻辑不变)](#3. 发布者(逻辑不变))
    • [4. 订阅者+测试(支持Lambda)](#4. 订阅者+测试(支持Lambda))
    • 无继承版优势
    • 通用泛型事件总线简化版

核心结论

委托是语言层面的语法/类型基础,事件是基于委托封装的安全通信机制,发布-订阅(观察者模式)是设计思想/架构模式,事件是发布-订阅在C#等主流面向对象语言里最典型的落地实现。


一、逐个定义(从底层到上层)

1. 委托(Delegate)

  • 定位:C# 特有语言级「方法指针/函数容器」,Java 无原生委托,用接口替代
  • 本质:引用类型,用于存储、传递签名匹配的方法
  • 作用:解耦调用方与执行方,支持动态挂载方法
  • 特点:支持多播;无业务语义、无安全限制,外部可直接赋值、清空、调用

2. 事件(Event)

  • 定位:基于委托封装的受保护消息通知机制,C# 关键字,依赖委托实现
  • 底层:委托的语法糖+访问权限控制
  • 核心约束:仅定义类内部可触发;外部仅支持 += 订阅、-= 取消订阅
  • 语义:专用于「动作发生时通知所有订阅者」

3. 发布-订阅模式(Publish-Subscribe/PubSub)

  • 定位:GoF 观察者模式,通用架构思想,跨语言
  • 角色:发布者(主题/Subject)推送消息;订阅者(观察者/Observer)接收并处理
  • 核心:完全解耦,双方仅依赖统一通知接口
  • 分类:进程内(UI事件、组件回调);分布式(RabbitMQ、Kafka、Redis PubSub)

二、三者层级&从属关系

  1. 层级从低到高:委托(语言基础)→ 事件(委托安全封装)→ 发布-订阅(设计模式)
  2. 委托是地基:C# 事件依赖委托实现
  3. 事件是委托安全增强版:锁住权限,仅开放订阅/取消订阅
  4. 事件是进程内发布-订阅标准实现:C# 中 event 定义+内部触发+外部 += 完全匹配观察者模式

一句话串联:C# 用委托做方法回调,用事件做权限封装,用事件落地进程内发布-订阅模式


三、关键对比(易混淆点)

1. 委托 vs 事件

维度 委托(Delegate) 事件(Event)
关系 事件底层类型 基于委托封装
访问权限 外部可赋值、调用、清空 仅内部可触发,外部仅 +=/-=
语义 通用方法容器 专用于消息通知
场景 通用函数传递、简单回调 UI事件、组件通知、发布订阅
csharp 复制代码
// 委托定义
public delegate void MsgDelegate(string msg);
// 裸委托
public MsgDelegate NormalDelegate;
// 事件
public event MsgDelegate MsgEvent;

2. 事件 vs 发布-订阅

  • 范畴:发布-订阅是设计思想;事件是C#语法实现
  • 范围:发布-订阅覆盖进程内+分布式;C#事件仅限当前进程/AppDomain
  • 实现:纯观察者手动维护列表;C#事件由编译器自动维护

3. 委托≠发布-订阅

委托仅为方法载体,仅封装为「订阅+通知」结构时才构成发布-订阅


四、完整运行逻辑(C# 点击事件示例)

  1. 预定义 ClickEventHandler 委托(约定签名)
  2. Button 类定义 event ClickEventHandler Click
  3. 订阅者用 button.Click += OnButtonClick 订阅
  4. 点击时按钮内部触发事件
  5. 所有订阅方法依次执行

流程:委托定签名 → 事件做权限封装 → 构成发布-订阅模式


五、其他语言对应关系

  • Java:无委托,用接口(Observer/Listener)替代,手动维护订阅集合
  • JavaScript:函数一等公民等价委托,addEventListener 等价事件
  • C++:函数指针/仿函数≈委托,手动封装事件机制

六、极简背诵总结

  1. 委托:C# 存放方法的底层工具
  2. 事件:委托加权限控制,专用于消息通知
  3. 发布-订阅:解耦通知的设计思想
  4. 关系:事件基于委托,是C#进程内发布-订阅标准实现

Java 实现基于委托+事件思想的发布-订阅(标准版)

场景

订单创建后通知库存扣减、短信发送、日志记录

1. 订单事件对象(继承EventObject)

java 复制代码
import java.util.EventObject;

public class OrderCreatedEvent extends EventObject {
    private final String orderId;
    private final String userId;
    private final double amount;

    public OrderCreatedEvent(Object source, String orderId, String userId, double amount) {
        super(source);
        this.orderId = orderId;
        this.userId = userId;
        this.amount = amount;
    }

    public String getOrderId() { return orderId; }
    public String getUserId() { return userId; }
    public double getAmount() { return amount; }
}

2. 事件监听器接口(模拟委托,继承EventListener)

java 复制代码
import java.util.EventListener;

@FunctionalInterface
public interface OrderCreatedListener extends EventListener {
    void onOrderCreated(OrderCreatedEvent event);
}

3. 发布者(订单服务)

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

public class OrderService {
    private final List<OrderCreatedListener> listeners = new CopyOnWriteArrayList<>();

    // 订阅(+=)
    public void addOrderCreatedListener(OrderCreatedListener listener) {
        if (listener != null) listeners.add(listener);
    }

    // 取消订阅(-=)
    public void removeOrderCreatedListener(OrderCreatedListener listener) {
        if (listener != null) listeners.remove(listener);
    }

    // 私有触发(仅内部可调用)
    private void fireOrderCreatedEvent(OrderCreatedEvent event) {
        for (OrderCreatedListener listener : listeners) {
            try {
                listener.onOrderCreated(event);
            } catch (Exception e) {
                System.err.println("事件处理异常:" + e.getMessage());
            }
        }
    }

    // 业务方法
    public void createOrder(String orderId, String userId, double amount) {
        System.out.println("【订单服务】创建订单成功,订单ID:" + orderId);
        fireOrderCreatedEvent(new OrderCreatedEvent(this, orderId, userId, amount));
    }
}

4. 订阅者实现

java 复制代码
// 库存服务
public class InventoryService implements OrderCreatedListener {
    @Override
    public void onOrderCreated(OrderCreatedEvent event) {
        System.out.println("【库存服务】扣减库存,订单ID:" + event.getOrderId());
    }
}

// 短信服务
public class SmsService implements OrderCreatedListener {
    @Override
    public void onOrderCreated(OrderCreatedEvent event) {
        System.out.println("【短信服务】发送短信给用户:" + event.getUserId());
    }
}

// 日志服务
public class LogService implements OrderCreatedListener {
    @Override
    public void onOrderCreated(OrderCreatedEvent event) {
        System.out.println("【日志服务】记录订单:" + event.getOrderId() + ",金额:" + event.getAmount());
    }
}

5. 测试主类

java 复制代码
public class PubSubDemo {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        InventoryService inventory = new InventoryService();
        SmsService sms = new SmsService();
        LogService log = new LogService();

        orderService.addOrderCreatedListener(inventory);
        orderService.addOrderCreatedListener(sms);
        orderService.addOrderCreatedListener(log);

        System.out.println("===== 第一次创建订单 =====");
        orderService.createOrder("ORDER_20240520_001", "USER_1001", 99.9);

        System.out.println("\n===== 取消短信订阅 =====");
        orderService.removeOrderCreatedListener(sms);

        System.out.println("\n===== 第二次创建订单 =====");
        orderService.createOrder("ORDER_20240520_002", "USER_1002", 199.9);
    }
}

运行结果

复制代码
===== 第一次创建订单 =====
【订单服务】创建订单成功,订单ID:ORDER_20240520_001
【库存服务】扣减库存,订单ID:ORDER_20240520_001
【短信服务】发送短信给用户:USER_1001
【日志服务】记录订单:ORDER_20240520_001,金额:99.9

===== 取消短信订阅 =====

===== 第二次创建订单 =====
【订单服务】创建订单成功,订单ID:ORDER_20240520_002
【库存服务】扣减库存,订单ID:ORDER_20240520_002
【日志服务】记录订单:ORDER_20240520_002,金额:199.9

C#与Java关键对应

C# 概念 Java 实现 说明
delegate 委托 函数式监听器接口 统一方法签名契约
event 事件 List<监听器>+add/remove 维护订阅列表
事件触发 私有fireXXX方法 仅发布者内部可调用
多播委托 遍历列表调用 手动实现多播

核心设计要点

  • 解耦:发布者与订阅者仅依赖接口与事件对象
  • 线程安全:CopyOnWriteArrayList
  • 异常隔离:单个订阅者异常不影响整体
  • 数据只读:事件仅提供getter
  • 权限控制:触发方法私有

拓展方向

泛型事件、异步处理、事件总线、事件优先级


Java 纯手动实现(无继承EventObject/EventListener)

1. 纯事件数据类(无继承)

java 复制代码
public class OrderCreatedEvent {
    private final String orderId;
    private final String userId;
    private final double amount;
    private final Object source;

    public OrderCreatedEvent(Object source, String orderId, String userId, double amount) {
        this.source = source;
        this.orderId = orderId;
        this.userId = userId;
        this.amount = amount;
    }

    public String getOrderId() { return orderId; }
    public String getUserId() { return userId; }
    public double getAmount() { return amount; }
    public Object getSource() { return source; }
}

2. 纯函数式监听器接口(无继承)

java 复制代码
@FunctionalInterface
public interface OrderCreatedListener {
    void onOrderCreated(OrderCreatedEvent event);
}

3. 发布者(逻辑不变)

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

public class OrderService {
    private final List<OrderCreatedListener> listeners = new CopyOnWriteArrayList<>();

    public void addListener(OrderCreatedListener listener) {
        if (listener != null) listeners.add(listener);
    }

    public void removeListener(OrderCreatedListener listener) {
        if (listener != null) listeners.remove(listener);
    }

    private void fireOrderCreatedEvent(OrderCreatedEvent event) {
        for (OrderCreatedListener listener : listeners) {
            try {
                listener.onOrderCreated(event);
            } catch (Exception e) {
                System.err.println("事件处理异常:" + e.getMessage());
            }
        }
    }

    public void createOrder(String orderId, String userId, double amount) {
        System.out.println("【订单服务】创建订单成功,订单ID:" + orderId);
        fireOrderCreatedEvent(new OrderCreatedEvent(this, orderId, userId, amount));
    }
}

4. 订阅者+测试(支持Lambda)

java 复制代码
// 传统订阅者
public class InventoryService implements OrderCreatedListener {
    @Override
    public void onOrderCreated(OrderCreatedEvent event) {
        System.out.println("【库存服务】扣减库存,订单ID:" + event.getOrderId());
    }
}

public class LogService implements OrderCreatedListener {
    @Override
    public void onOrderCreated(OrderCreatedEvent event) {
        System.out.println("【日志服务】记录金额:" + event.getAmount());
    }
}

// 测试类
public class PurePubSubDemo {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        InventoryService inventory = new InventoryService();
        LogService log = new LogService();

        orderService.addListener(inventory);
        orderService.addListener(log);
        // Lambda订阅
        orderService.addListener(event -> 
            System.out.println("【短信服务】发送给:" + event.getUserId())
        );

        System.out.println("===== 第一次创建订单 =====");
        orderService.createOrder("ORDER_001", "USER_1001", 99.9);

        System.out.println("\n===== 取消库存订阅 =====");
        orderService.removeListener(inventory);

        System.out.println("\n===== 第二次创建订单 =====");
        orderService.createOrder("ORDER_002", "USER_1002", 199.9);
    }
}

无继承版优势

  • 代码纯粹,无多余依赖
  • 完美支持Lambda
  • 无Java标准事件模型限制
  • 学习成本低,灵活度高

通用泛型事件总线简化版

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

public class EventPublisher<T> {
    private final List<EventListener<T>> listeners = new CopyOnWriteArrayList<>();

    public void addListener(EventListener<T> listener) {
        if (listener != null) listeners.add(listener);
    }

    public void removeListener(EventListener<T> listener) {
        if (listener != null) listeners.remove(listener);
    }

    public void fireEvent(T event) {
        for (EventListener<T> listener : listeners) {
            try {
                listener.onEvent(event);
            } catch (Exception e) {
                System.err.println("事件处理异常:" + e.getMessage());
            }
        }
    }
}
相关推荐
坏柠1 小时前
从一个设备控制面板开始,系统学习 LVGL 界面开发
android·javascript·学习
MartinYeung52 小时前
[论文学习] 全同态加密下的加密文字比较与子字串搜寻演算法延伸研究
学习·区块链·同态加密
Front思2 小时前
如何学习Shopify前端开发?
前端·学习
再玩一会儿看代码2 小时前
Java浅拷贝和深拷贝理解笔记
java·linux·开发语言·笔记·python·学习
我命由我123452 小时前
Excel - Excel 查看当前单元格格式
运维·学习·职场和发展·excel·求职招聘·职场发展·学习方法
花落yu2 小时前
AI学习:第4天
学习
壹号用户2 小时前
缺省参数和函数重载
c++·学习
MartinYeung52 小时前
[论文学习]利用自学习激活函数强化全同态加密下的隐私保护机器学习
学习·机器学习·同态加密
颂love2 小时前
Vue3基础入门
前端·学习·vue3