C# 观察者模式

一、概述

观察者模式是一种常用的设计模式,它属于行为型模式。在C#中,观察者模式通过定义一种一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。这种模式可以实现松耦合,使得被观察者和观察者之间的关系更加灵活。

在C#中实现观察者模式通常需要以下几个角色:

1. Subject(主题):被观察者,它维护了一个观察者列表,并提供了添加、删除和通知观察者的方法。

2. Observer(观察者):观察者,它定义了一个更新方法,用于接收被观察者发出的通知。

3. ConcreteSubject(具体主题):具体的被观察者,它继承或实现了主题接口,并实现了具体的业务逻辑。它会在自身状态发生变化时通知观察者。

4. ConcreteObserver(具体观察者):具体的观察者,它继承或实现了观察者接口,并实现了更新方法。当接收到被观察者的通知时,它会执行相应的逻辑。

通过使用观察者模式,我们可以实现对象之间的解耦,使得它们之间的依赖关系更加灵活和可扩展。这种模式在事件处理、GUI开发以及许多其他场景中都有广泛应用。

观察者模式的优点和缺点:

观察者模式的优点:

1. 松耦合:被观察者和观察者之间的关系是松耦合的,它们可以独立变化而互不影响。

2. 可扩展性:可以方便地增加新的观察者,或者在不影响现有代码的情况下增加新的被观察者。

3. 易于维护:观察者模式将业务逻辑分散到各个观察者中,使得代码更加清晰、易于维护。

4. 支持广播通信:被观察者可以同时通知多个观察者,实现广播式的通信。

观察者模式的缺点:

1. 观察者过多时的性能问题:如果观察者过多或者观察者的更新操作比较耗时,可能会影响系统的性能。

2. 循环依赖问题:观察者和被观察者之间存在循环依赖的情况下,可能导致系统出现问题。

3. 更新顺序不确定:观察者模式中,观察者的更新顺序是不确定的,可能会导致一些意外的结果。

观察者模式适用于以下场景:

1. 当一个对象的状态变化需要通知其他多个对象,并且这些对象的行为需要根据该状态变化做出相应的调整时,可以使用观察者模式。

2. 当一个对象需要在不知道有多少个其他对象关注它的情况下,动态地将消息通知给这些对象时,可以使用观察者模式。

3. 当一个对象的改变需要同时影响其他多个对象,并且它不希望与这些对象形成紧耦合关系时,可以使用观察者模式。

4. 当系统中的某个对象需要与其他多个对象进行解耦,以降低对象之间的依赖性时,可以使用观察者模式。

5. 当需要实现事件驱动的系统或者消息通知机制时,可以使用观察者模式。

观察者模式适用于多个对象之间存在一对多的依赖关系,当一个对象的状态发生变化时,需要通知其他多个对象进行相应的处理。它能够实现对象之间的解耦,提高系统的灵活性和可扩展性。

二、代码实现

新建一个控制台项目

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ConcreteSubject subject = new ConcreteSubject();
            ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
            ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);
            ConcreteObserver observer3 = new ConcreteObserver("Observer 3", subject);

            subject.AddObserver(observer1);
            subject.AddObserver(observer2);
            subject.AddObserver(observer3);

            subject.State = 1; // 触发通知
            subject.RemoveObserver(observer2);
            subject.State = 2; // 触发通知

            Console.ReadKey();
        }
    }

    // 主题接口
    public interface ISubject
    {
        void AddObserver(IObserver observer);
        void RemoveObserver(IObserver observer);
        void NotifyObservers();
    }

    // 具体主题
    public class ConcreteSubject : ISubject
    {
        private List<IObserver> observers = new List<IObserver>();
        private int state;

        public int State
        {
            get { return state; }
            set
            {
                state = value;
                NotifyObservers();
            }
        }

        public void AddObserver(IObserver observer)
        {
            observers.Add(observer);
        }

        public void RemoveObserver(IObserver observer)
        {
            observers.Remove(observer);
        }

        public void NotifyObservers()
        {
            foreach (IObserver observer in observers)
            {
                observer.Update();
            }
        }
    }

    // 观察者接口
    public interface IObserver
    {
        void Update();
    }

    // 具体观察者
    public class ConcreteObserver : IObserver
    {
        private string name;
        private ConcreteSubject subject;

        public ConcreteObserver(string name, ConcreteSubject subject)
        {
            this.name = name;
            this.subject = subject;
        }

        public void Update()
        {
            Console.WriteLine($"Observer {name} received an update. New state: {subject.State}");
        }
    }
}

运行:

三、解析代码

上面的代码看起来比较复杂,在23个设计模式中,其实还不算最复杂的,看多了习惯就好,下面大致的讲下代码逻辑。

首先是定义了一个接口 ISubject,它有三个方法,添加观察者,移除观察者,通告观察者。

然后 ConcreteSubject 继承了这个接口,这里重点在 state 这个属性这里:

public int State
{
    get { return state; }
    set
    {
        state = value;
        NotifyObservers();
    }
}

如果设置属性的值,就会调用 NotifyObservers 方法,这是个通知所有观察者的一个方法。

ConcreteObserver 存储了 name 和 具体观察者 ConcreteSubject 的实例(具体的主题),不过这里,只是用到了打印 subject.State ,并无其他作用。

Console.WriteLine($"Observer {name} received an update. New state: {subject.State}");

在 ConcreteSubject.AddObserver 方法的参数是一个 IObserver 接口,其实传递的就是 ConcreteObserver 实例,这里由于只需要调用 Update 方法,所以只用到了一个接口 IObserver,虽然保护了 ConcreteObserver 类的开放权限,但使的整个过程看起来更加复杂了。

从工作的角度来说,上面的很多写法并不是那么推荐,将简单的事情复杂化,是在自己在刁难自己,还可能会导致更多的 bug,可别忘了公司里还有项目经理,老板,他们可是一直在催你快点做,他们可不管你代码写的怎么样,他们根本就不懂代码。

在 Main 函数中,实例化了三个 ConcreteObserver 类,并传入了 name , 并添加到了 subject 中,这就是添加了三个观察者,以便后面用消息来通知他们

在设置 subject.State = 1 时,默认就调用了 NotifyObservers 方法,也就调用了三个观察者的 Update 方法,理解了,就会发现这些代码其实没有那么难。

四、案例

下面的代码来源博客 JiYF大男孩,写的很不错,拿来做个参考,链接在下面

https://www.cnblogs.com/JiYF/p/6896458.html

新建类 Blog

using System.Collections.Generic;

/// <summary>  
/// 订阅者接口  
/// </summary>  
public interface IObserver
{
    void Receive(Blog blog);
}

/// <summary>  
/// 订阅博客抽象类  
/// </summary>  
public abstract class Blog
{
    /// <summary>  
    /// 保存订阅者列表  
    /// </summary>  
    private List<IObserver> observers = new List<IObserver>();

    /// <summary>  
    /// 博主名  
    /// </summary>  
    public string BlogName { get; set; }

    /// <summary>  
    /// 博客标题  
    /// </summary>  
    public string BlogTitle { get; set; }

    /// <summary>  
    /// 博客信息  
    /// </summary>  
    public string BlogInfo { get; set; }

    /// <summary>  
    /// 博客构造函数  
    /// </summary>  
    /// <param name="blogTitle">博客标题</param>  
    /// <param name="blogInfo">博客信息</param>  
    public Blog(string name, string blogTitle, string blogInfo)
    {
        this.BlogName = name;
        this.BlogTitle = blogTitle;
        this.BlogInfo = blogInfo;
    }

    /// <summary>  
    /// 添加一个订阅者  
    /// </summary>  
    /// <param name="observer">具体的订阅者对象</param>  
    public void AddObserver(IObserver observer)
    {
        if (observers.Contains(observer))
        {
            return;
        }
        observers.Add(observer);
    }

    /// <summary>  
    /// 删除一个订阅者  
    /// </summary>  
    /// <param name="observer">具体的订阅者对象</param>  
    public void RemoveObserver(IObserver observer)
    {
        if (observers.Contains(observer))
        {
            observers.Remove(observer);
        }
    }

    /// <summary>  
    /// 发布博客通知  
    /// </summary>  
    public void PublishBlog()
    {
        //遍历通知每一个订阅者  
        foreach (IObserver ob in observers)
        {
            if (ob != null)
            {
                // 调用继承当前接口的Receive方法  
                ob.Receive(this);
            }
        }
    }
}  
 

新建类 JiYFBlog

namespace 设计模式_观察者模式
{
    /// <summary>  
    /// 具体的订阅博客类  
    /// </summary>  
    public class JiYFBlog : Blog
    {
        public JiYFBlog(string name, string blogTitile, string blogInfo)
            : base(name, blogTitile, blogInfo)
        {

        }
    }
}

新建类 Observer

using System;

namespace 设计模式_观察者模式
{
    /// <summary>  
    /// 具体的订阅者类  
    /// </summary>  
    public class Observer : IObserver
    {
        /// <summary>  
        /// 订阅者名字  
        /// </summary>  
        private string m_Name;
        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        /// <summary>  
        /// 订阅者构造函数  
        /// </summary>  
        /// <param name="name">订阅者名字</param>  
        public Observer(string name)
        {
            this.m_Name = name;
        }

        /// <summary>  
        /// 订阅者接受函数  
        /// </summary>  
        /// <param name="blog"></param>  
        public void Receive(Blog blog)
        {
            Console.WriteLine("订阅者:\"{0}\"观察到了:{1}发布的一篇博客,标题为:{2},内容为:{3}", Name, blog.BlogName, blog.BlogTitle, blog.BlogInfo);
        }
    }
}

调用方法

using System;

namespace 设计模式_观察者模式
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("--全部订阅者--");
            // 创建一个 JiYF 的博客  
            // 多态的方式发布一条播客,但此时还没有订阅者  
            Blog jiyfBlog = new JiYFBlog("JiYF笨小孩", "丑小鸭", "丑小鸭的故事");

            // 创建订阅者  
            Observer obsZhangsan = new Observer("张三");
            Observer obsLiSi = new Observer("李四");
            Observer obsWangwu = new Observer("王五");

            // 添加到 JiYF 博客的订阅者  
            jiyfBlog.AddObserver(obsZhangsan);
            jiyfBlog.AddObserver(obsLiSi);
            jiyfBlog.AddObserver(obsWangwu);

            //通知订阅者  
            jiyfBlog.PublishBlog();

            Console.WriteLine();
            Console.WriteLine("--移除订阅者张三--");
            jiyfBlog.RemoveObserver(obsZhangsan);
            jiyfBlog.PublishBlog();


            Console.ReadLine();
        }
    }
}

运行:

 

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/86689.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

精密图纸被窃,知名手表品牌Seiko遭BlackCat勒索软件攻击

据BleepingComputer消息&#xff0c;日本著名手表制造商Seiko在7月末遭到了网络攻击&#xff0c;8月21日&#xff0c;BlackCat&#xff08;又名ALPHV&#xff09;勒索软件组织在其网站上宣布对这起攻击事件负责。 8 月 10 日&#xff0c;Seiko发布了一份数据泄露通知&#xff0…

机器学习---常见的距离公式(欧氏距离、曼哈顿距离、标准化欧式距离、余弦距离、杰卡德距离、马氏距离、切比雪夫距离、闵可夫斯基距离、K-L散度)

1. 欧氏距离 欧几里得度量&#xff08;euclidean metric&#xff09;&#xff08;也称欧氏距离&#xff09;是一个通常采用的距离定义&#xff0c;指在m维空 间中两个点之间的真实距离&#xff0c;或者向量的自然长度&#xff08;即该点到原点的距离&#xff09;。在二维和三维…

【雷达】接收和去噪L波段雷达接收到的信号研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

单链表的多语言表达:C++、Java、Python、Go、Rust

单链表 是一种链式数据结构&#xff0c;由一个头节点和一些指向下一个节点的指针组成。每个节点包含一个数据元素和指向下一个节点的指针。头节点没有数据&#xff0c;只用于表示链表的开始位置。 单链表的主要操作包括&#xff1a; 添加元素&#xff1a;在链表的头部添加新…

采用typescript编写,实现ofd前端预览、验章

前言 浏览器内核已支持pdf文件的渲染&#xff0c;这极大的方便了pdf文件的阅读和推广。ofd文件作为国产板式标准&#xff0c;急需一套在浏览器中渲染方案。 本人研究ofd多年&#xff0c;分别采用qt、c# 开发了ofd阅读器。本人非前端开发人员&#xff0c;对js、typescript并不熟…

工程管理与工作流

1 统一开发环境/ 协作工具 你知道开发环境指的是什么吗&#xff1f; 开发环境&#xff1a; 工程运行环境、开发工具/ 编辑器 、开发依赖环境、 配置文件 软件环境&#xff1a; “仿真预演”环境 Staging 生产环境前最终验证、 这一环境尽可能的仿真了真实的生产环境 、另一个…

自己实现 SpringMVC 底层机制 系列之-实现任务阶段 6-完成控制器方法获取参数-@RequestParam

&#x1f600;前言 自己实现 SpringMVC 底层机制 系列之-实现任务阶段 6-完成控制器方法获取参数-RequestParam &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c…

网络编程——网络基础知识

目录 一、网络历史两个重要名词1.1 阿帕网1.2 TCP/IP协议 二、局域网和广域网三、IP地址3.1 基本概念3.2 划分(IPV4)3.3 特殊IP地址3.4 子网掩码3.5 重新组网 四、网络模型4.1 网络的体系结构&#xff1a;4.2 OSI与TCP/IP模型4.2.1 OSI模型4.2.2 TCP/IP模型4.2.3 OSI和TCP/IP模…

CNN之图像识别

什么是图像识别 • 图像识别技术是信息时代的一门重要的技术&#xff0c;其产生目的是为了让计算机代替人类去处理大量的物理信息。随着计算机技术的发展&#xff0c;人类对图像识别技术的认识越来越深刻 • 图像识别技术的定义为利用计算机对图像进行处理、分析和理解&…

Docker(二) Docker容器

在docker中的容器都是由镜像所创建的&#xff0c;一个镜像可以创建多个容器。 一、调试Docker 启动Docker systemctl start docker 查看Docker中有哪些镜像 docker images 下载镜像 docker pull hello-world 运行镜像 docker run hello-world 出现 Hello from Docker! 这…

构建系统自动化-autoreconf

autoreconf简介 autoreconf是一个GNU Autotools工具集中的一个命令&#xff0c;用于自动重新生成构建系统的配置脚本和相关文件。 Autotools是一组用于自动化构建系统的工具&#xff0c;包括Autoconf、Automake和Libtool。它们通常用于跨平台的软件项目&#xff0c;以便在不同…

[C语言]分支与循环

导言&#xff1a; 在人生中我们总会有选择&#xff0c;**如下一顿吃啥&#xff1f;**又或者每天都是在重复&#xff0c;吃饭&#xff01;&#xff01;&#xff01;&#xff01;&#xff0c;当然在C语言中也有选择和重复那就是分支语句与循环语句 文章目录 分支循环循环中的关键…

手写代码-前端面试

GitHub&#xff1a;手写代码集合

大数据-玩转数据-Flink营销对账

一、说明 在电商网站中&#xff0c;订单的支付作为直接与营销收入挂钩的一环&#xff0c;在业务流程中非常重要。对于订单而言&#xff0c;为了正确控制业务流程&#xff0c;也为了增加用户的支付意愿&#xff0c;网站一般会设置一个支付失效时间&#xff0c;超过一段时间不支…

一云多芯,智能化转型的下一个工程化挑战

进入2023年&#xff0c;产业数字化和智能化转型升级进入了大规模工程化落地阶段。根据中国信通院《中国数字经济发展研究报告&#xff08;2023&#xff09;》&#xff0c;数字经济已经占我国GDP比重达到41.5%&#xff0c;相当于第二产业占国民经济的比重。随着产业数字化和智能…

【数据结构】 LinkedList的模拟实现与使用

文章目录 &#x1f340;什么是LinkedList&#x1f334;LinkedList的模拟实现&#x1f6a9;创建双链表&#x1f6a9;头插法&#x1f6a9;尾插法&#x1f6a9;任意位置插入&#x1f6a9;查找关键字&#x1f6a9;链表长度&#x1f6a9;打印链表&#x1f6a9;删除第一次出现关键字为…

YOLOv5+deepsort实现目标追踪。(附有各种错误解决办法)

一、YOLOv5算法相关配置 🐸这里如果是自己只想跑一跑YOLOV5的话,可以参考本章节。只想跑通YOLOv5+deepsort的看官移步到下一章节。 1.1 yolov5下载 🐸yolov5源码在github下载地址上或者Gitee上面都有。需要注意的是由于yolov5的代码库作者一直在维护,所以下载的时候需…

【Unity小技巧】Unity探究自制对象池和官方内置对象池(ObjectPool)的使用

文章目录 前言不使用对象池使用官方内置对象池应用 自制对象池总结源码参考完结 前言 对象池&#xff08;Object Pool&#xff09;是一种软件设计模式&#xff0c;用于管理和重用已创建的对象。在对象池中&#xff0c;一组预先创建的对象被维护在一个池中&#xff0c;并在需要时…

OJ练习第152题——分割回文串 II

分割回文串 II 力扣链接&#xff1a;132. 分割回文串 II 题目描述 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文。 返回符合要求的 最少分割次数 。 示例 Java代码 class Solution {public int minCut(String s) {int n s.leng…

听说你还不知道什么是python?本文将带你发掘python的魅力并让你爱上他

文章目录 前言什么是pythonpython的由来我们为什么要学习python帮助python学习的网站总结 前言 各位朋友们&#xff0c;大家好。龙叔我后台经常收到私信问什么是Python&#xff1f;有必要学习这门语言么&#xff1f;今天&#xff0c;将通过本文告知大家Python是什么&#xff1…
最新文章