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 = "張三"});

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章