观察者模式

回到首页

概述

也被称为发布订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己

观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子(邮件订阅、RSS Feeds,本质上都是观察者模式。)

不同的应用场景和需求下,这个模式也有截然不同的实现方式,有同步阻塞的实现方式,也有异步非阻塞的实现方式;有进程内的实现方式,也有跨进程的实现方式。

跨进程之间的观察者可以通过RPC(grpc框架)或者消息队列的方式进行实现

结构图

代码实现

同步实现

主题

复制代码
    /// <summary>
    /// 抽象主题
    /// </summary>
   public abstract class Subject
    {
        private IList<Observer> observers = new List<Observer>();

        //增加观察者
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }
        //移除观察者
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }
        //通知
        public void Notify()
        {
            foreach (Observer o in observers)
            {
                o.Update();
            }
        }
    }
复制代码
复制代码
    /// <summary>
    ///具体主题
    /// </summary>
    public class ConcreteSubject : Subject
    {
        private string subjectState;

        //具体通知者状态
        public string SubjectState
        {
            get { return subjectState; }
            set { subjectState = value; }
        }
    }
复制代码

观察者

    /// <summary>
    /// 抽象观察者
    /// </summary>
    public abstract class Observer
    {
        public abstract void Update();
    }
复制代码
    /// <summary>
    /// 具体观察者
    /// </summary>
    public class ConcreteObserver : Observer
    {
        private string name;
        private string observerState;
        private ConcreteSubject subject;

        public ConcreteObserver(ConcreteSubject subject, string name)
        {
            this.subject = subject;
            this.name = name;
        }
        //更新
        public override void Update()
        {
            observerState = subject.SubjectState;
            Console.WriteLine("观察者{0}的新状态是{1}", name, observerState);
        }

        public ConcreteSubject Subject
        {
            get { return subject; }
            set { subject = value; }
        }
    }
复制代码

客户端

复制代码
    class Program
    {
        static void Main(string[] args)
        {
            ConcreteSubject s = new ConcreteSubject();

            s.Attach(new ConcreteObserver(s, "X"));
            s.Attach(new ConcreteObserver(s, "Y"));
            s.Attach(new ConcreteObserver(s, "Z"));

            s.SubjectState = "ABC";
            s.Notify();

            Console.Read();
        }
    }
复制代码

运行结果

简单的非阻塞异步

主要思路就是在进行执行通知订阅信息操作时使用线程

但是若非常频繁的调用这个方法,就会频繁的创建和销毁线程,若线程的创建速度大于销毁速度就有可能出现内存溢出的问题

具体主题

复制代码
    /// <summary>
    ///具体主题
    /// </summary>
    public class AsyncConcreteSubject : Subject
    {
        private string subjectState;

        //具体通知者状态
        public string SubjectState
        {
            get { return subjectState; }
            set { subjectState = value; }
        }

        public override void Notify()
        {
            //执行耗时操作
            Thread.Sleep(1000);
            Task.Run(() =>
            {
                base.Notify();
            });
            Console.WriteLine("具体主题发送完成了通知");
        }
    }
复制代码

EventBus的方式

EventBus类

复制代码
    public class EventBus
    {
        private List<object> observers = new List<object>();
        public void Attach(object obj)
        {
            observers.Add(obj);
        }
        public void Detach(object obj)
        {
            observers.Remove(obj);
        }
        public void Notify(object parameter)
        {
            foreach (var observer in observers)
            {
                Type type = observer.GetType();
                MethodInfo[] methods = type.GetMethods();
                foreach (var method in methods)
                {
                    EventBusFunctionAttribute attribute = method.GetCustomAttribute<EventBusFunctionAttribute>();
                    if (attribute != null)
                    {
                        if (attribute.ParameterType == parameter.GetType())
                        {
                            method.Invoke(observer, new object[] { parameter });
                        }
                    }
                }
            }
        }
    }
复制代码

特性标记类

复制代码
    /// <summary>
    /// 用于标记观察者执行的方法
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class EventBusFunctionAttribute : Attribute
    {
        private Type parameterType;
        public Type ParameterType { get { return parameterType; } }
        public EventBusFunctionAttribute(Type parameterType)
        {
            this.parameterType = parameterType;
        }
    }
复制代码

观察者

复制代码
    public class Subject1
    {
        public string c1 { get; set; }
        public string c2 { get; set; }
    }
    public class Concrete1Observer
    {
        [EventBusFunction(typeof(Subject1))]
        public void Update(object obj)
        {
            Subject1 subject1 = obj as Subject1;
            Console.WriteLine("Concrete1Observer接收到数据" + subject1.c1 + "," + subject1.c2);
        }
    }
复制代码
复制代码
    public class Subject2
    {
        public string c3 { get; set; }
        public string c4 { get; set; }
    }
    public class Concrete2Observer
    {
        [EventBusFunction(typeof(Subject2))]
        public void Operation(object obj)
        {
            Subject2 subject2 = obj as Subject2;
            Console.WriteLine("Concrete2Observer接收到数据" + subject2.c3 + "," + subject2.c4);
        }
    }
复制代码

客户端

复制代码
        static void Main(string[] args)
        {
            EventBus eventBus = new EventBus();
            Concrete1Observer concrete11Observer = new Concrete1Observer();
            Concrete1Observer concrete12Observer = new Concrete1Observer();
            Concrete2Observer concrete2Observer = new Concrete2Observer();
            eventBus.Attach(concrete11Observer);
            eventBus.Attach(concrete12Observer);
            eventBus.Attach(concrete2Observer);
            eventBus.Notify(new Subject1()
            {
                c1 = "1111",
                c2 = "2222",
            });
            eventBus.Notify(new Subject2()
            {
                c3 = "3333",
                c4 = "4444",
            });
            Console.Read();
        }
复制代码

运行结果

 

 

优势

降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则

目标与观察者之间建立了一套触发机制。

使用场景

一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。

需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

缺陷

如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

Demo

posted @   .NET_CJL  阅读(60)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示