观察者模式的"最佳实践"
记录一次观察者模式的使用,目前需求是这样的,我有很多个模块,模块与模块之间是相互独立的,当我某个地方触发到相应的逻辑或状态时,需要让其他所有模块都去做自己相应的逻辑处理,文章中有删减,大致是这么个实现思路。
一、代码实现过程
1、为方便其他地方直接调用,这里直接使用静态类,主要提供3个对外的方法,分别是注册监听、取消监听以及发布。
2、创建一个订阅者类,
private class Listener
{
/// <summary>
/// 编号
/// </summary>
public int Id { get; set; }
/// <summary>
/// 事件处理钩子
/// </summary>
public EventHandler Handler { get; set; }
}
注意这里EventHandler是一个委托,方便我们后续直接执行操作方法,我们需要声明以下
public delegate void EventHandler();
3、再创建一个字典用于存储订阅者们,我这里是使用一个枚举作为Key,一个列表作为Value,实际视具体业务而定
/// <summary>
/// 事件映射
/// </summary>
private static Dictionary<EventEnum, List<Listener>> events = new Dictionary<EventEnum, List<Listener>>();
4、编写注册监听的逻辑
public static void Register(int id, EventEnum eventEnum, EventHandler handler)
{
if (!events.ContainsKey(eventEnum))
{
List<Listener> listeners = new List<Listener>();
listeners.Add(new Listener() { Id = id, Handler = handler });
events.Add(eventEnum, listeners);
}
else
{
List<Listener> listeners = events[eventEnum];
listeners.Add(new Listener() { Id = id, Handler = handler });
}
}
首先检查字典中有无这个key,如果没有才注册(添加)进去,List列表中就是所有订阅者
5、编写取消注册监听
public static void Unregister(int id, EventEnum eventEnum, EventHandler handler)
{
if (events.ContainsKey(eventEnum))
{
List<Listener> listeners = events[eventEnum];
for (int i = listeners.Count; i > 0; i--)
{
Listener listener = listeners[i - 1];
if (listener.Id.Equals(id) && listener.Handler.GetType() == handler.GetType())
{
listeners.Remove(listener);
}
}
}
}
大致思路就是这样,如果要取消的键存在,我们则获取到字典中该枚举的所有的订阅者,然后去遍历,如果id和handler吻合,就从字典中移除掉
6、最后再实现发布订阅,用于通知已注册监听的订阅者
public static void Publish(int id, EventEnum eventEnum)
{
if (events.ContainsKey(eventEnum))
{
List<Listener> listeners = events[eventEnum];
foreach (var item in listeners)
{
if (item.Id.Equals(id))
{
item.Handler();
}
}
}
}
就是遍历,然后逐个执行刚才那个委托。
7、最后就是使用了,为了简化,这里直接简单记录一下,后续根据实际业务情况去对应的地方注册和发布就完事了
public class Program
{
static void Main(string[] args)
{
//订阅监听
SubscriberManager.Register(1, EventEnum.Status1, HandlerMethod);
SubscriberManager.Register(2, EventEnum.Status3, HandlerMethod2);
//发布
SubscriberManager.Publish(1, EventEnum.Status1);
//SubscriberManager.Publish(2, EventEnum.Status3);
Console.ReadKey();
}
//监听到发布事件后所要执行的处理方法
private static void HandlerMethod2()
{
Console.WriteLine("执行方法2");
}
private static void HandlerMethod()
{
Console.WriteLine("执行方法1");
}
}
总结一下:观察者模式很适合当一个状态发生改变时,需要通知到多处的使用场景,实现起来也比较简单易理解。
对应的代码再Github仓库:https://github.com/luchong0813/DesignModel/tree/main/11-Listener