12.NET观察者模式,发布与订阅
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 ——发布订阅模式
发布订阅模式主要有两个角色:
1.发布方(Publisher):也称为被观察者,当状态改变时负责通知所有订阅者。
2.订阅方(Subscriber):也称为观察者,订阅事件并对接收到的事件进行处理。
发布订阅模式有两种实现方式:
1.简单的实现方式:由Publisher维护一个订阅者列表,当状态改变时循环遍历列表通知订阅者。
2.委托的实现方式:由Publisher定义事件委托,Subscriber实现委托。
简单地实现发布订阅案例
public class Dog { public void Roar() { Console.WriteLine("汪汪汪!再不走咬你了"); } }
public class Person { public void Roar() { Console.WriteLine("小偷,哪里跑"); } }
发布:
/// <summary> /// 小偷 /// </summary> public class Thief { public delegate void StealDelegate();//委托 /// <summary> /// 偷盗事件 /// </summary> public event StealDelegate StealEvent;//事件 /// <summary> /// 偷 /// </summary> public void Steal() { Console.WriteLine("哇,好多钱呀"); StealEvent.Invoke(); // 触发事件,发布 } }
订阅:
Thief thief = new(); // 狗和主人,同时订阅偷盗,并观察有没有小偷进来 thief.StealEvent+= new Dog().Roar; thief.StealEvent+=new Person().Roar; // 开始偷盗 thief.Steal(); Console.ReadKey();
如何实现自动订阅发布
发布订阅模式下,无非就涉及到三种实体:
1.事件发布者(EventBus)
2.事件处理者/订阅者(EventHandler)
3.事件源(EventData, 用于传参)
事件源,参数:
/// <summary> /// 事件源 /// </summary> public class EventData { /// <summary> /// 事件源 /// </summary> public object? Sender { get; set; } /// <summary> /// 事件发布的时间 /// </summary> public DateTime CreateTime { get; set; } = DateTime.Now; }
订阅接口:
/// <summary> /// 事件处理者(所有的订阅者都需要继承只接口) /// 还有一个作用:凡是继承了这个接口的人,都是订阅者 /// </summary> public interface IEventHandler<in TEventData> where TEventData:EventData { /// <summary> /// 事件处理的方法 /// </summary> void Handler(TEventData data); }
发布接口:
/// <summary> /// 事件发布者 /// </summary> public interface IEventBus<in TEventData> where TEventData:EventData { void Publish(TEventData data); }
实现IEventBus
/// <summary> /// 事件总线实现者(所有的发布者都用它) /// 这个类需要实现两个功能: /// 1. 自动订阅 /// 2. 订阅者需要自动触发 /// </summary> public class EventBus<TEventData>:IEventBus<TEventData> where TEventData:EventData { public delegate void EventBusHandler(TEventData data); public event EventBusHandler EventBusEvent; /// <summary> /// 每种EventData 可能对应一组EventHandler(订阅者) /// </summary> private static readonly Dictionary<Type, List<Type>> EventHandlerMapping = new(); static EventBus() { // 获取所有实现了IEventHandler<>接口的子类 var list = (from type in Assembly.GetExecutingAssembly().GetTypes() from intf in type.GetInterfaces() where intf.IsGenericType && intf.GetGenericTypeDefinition() == typeof(IEventHandler<>) select type).ToList(); foreach (var t in list) { //获取该类实现的泛型接口 Type? handlerInterface = t.GetInterface("IEventHandler`1"); if (handlerInterface != null) { // 获取泛型接口指定的EventData参数类型 Type eventDataType = handlerInterface.GetGenericArguments()[0]; if (EventHandlerMapping.ContainsKey(eventDataType)) { List<Type> handlerTypes = EventHandlerMapping[eventDataType]; handlerTypes.Add(t); EventHandlerMapping[eventDataType] = handlerTypes; } else { var handlerTypes = new List<Type> {t}; EventHandlerMapping[eventDataType] = handlerTypes; } } } } /// <summary> /// 发布事件, 订阅者自动触发 /// </summary> /// <param name="data"></param> /// <exception cref="NotImplementedException"></exception> public void Publish(TEventData data) { var eventHandlerList = EventHandlerMapping[data.GetType()]; foreach (var t in eventHandlerList) { // 自动订阅事件 EventBusEvent += (Activator.CreateInstance(t) as IEventHandler<TEventData>)!.Handler; } // 触发事件 EventBusEvent?.Invoke(data); } }
案列1,无参订阅
public class Dog:IEventHandler<EventData> { public void Handler(EventData data) { Console.WriteLine("汪汪汪,再不走就咬你"); } }
public class Person:IEventHandler<EventData> { public void Handler(EventData data) { Console.WriteLine("小偷哪里逃"); } }
实现:
Console.WriteLine("Hello, World!"); // 事件源 EventData data = new(); // 创建发布者 IEventBus<EventData> bus = new EventBus<EventData>(); // 发布事件 bus.Publish(data);
案例2,有参订阅:
参数类
/// <summary> /// 消息通知 事件参数 /// </summary> public class NotifyEventData:EventData { /// <summary> /// 通知接收者 /// </summary> public string Receiver { get; set; } = null!; }
订阅,发送Email
public class EmailEventHandler:IEventHandler<NotifyEventData> { public void Handler(NotifyEventData data) { Console.WriteLine($"{data.Receiver} 收到了邮件通知"); } }
订阅,发送短信
public class SmsEventHandler:IEventHandler<NotifyEventData> { public void Handler(NotifyEventData data) { Console.WriteLine($"{data.Receiver} 收到了短信通知"); } }
发布
Console.WriteLine("Hello, World!"); IEventBus<NotifyEventData> bus2 = new EventBus<NotifyEventData>(); bus2.Publish(new NotifyEventData {Receiver = "张三"});