Loading

观察者模式的"最佳实践"

记录一次观察者模式的使用,目前需求是这样的,我有很多个模块,模块与模块之间是相互独立的,当我某个地方触发到相应的逻辑或状态时,需要让其他所有模块都去做自己相应的逻辑处理,文章中有删减,大致是这么个实现思路。

一、代码实现过程

1、为方便其他地方直接调用,这里直接使用静态类,主要提供3个对外的方法,分别是注册监听、取消监听以及发布。

image-20210817142823953

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

posted @ 2021-08-17 14:46  傲慢与偏见luc  阅读(706)  评论(0编辑  收藏  举报