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.rhkb.cn/news/97719.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s 自身原理之 Service

好不容易&#xff0c;终于来到 k8s 自身的原理之 关于 Service 的一部分了 前面我们用 2 个简图展示了 pod 之间和 pod 与 node 之间是如何通信息的&#xff0c;且通信的数据包是不会经过 NAT 网络地址转换的 那么 Service 又是如何实现呢&#xff1f; Service 我们知道是用…

常见的 Python 错误及其解决方案

此文整理了一些常见的 Python 错误及其解决方案。 1、SyntaxError: invalid syntax 说明&#xff1a;无效的语法是最常见的错误之一&#xff0c;通常是由于编写代码时违反了 Python 的语法规则。可能的原因&#xff1a; 忘记在 if、while、for 等语句后写冒号&#xff0c;或者…

VS2015打开Qt的pro项目文件 报错

QT报错&#xff1a;Project ERROR: msvc-version.conf loaded but QMAKE_MSC_VER isn‘t set 解决方法&#xff1a; 找到本机安装的QT路径&#xff0c;找到“msvc-version.conf”文件&#xff0c;用记事本打开&#xff0c; 在其中添加版本“QMAKE_MSC_VER 1900”保存即可。 …

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)八:自定义组件封装上

一、本章内容 本章实现一些自定义组件的封装,包括数据字典组件的封装、下拉列表组件封装、复选框单选框组件封装、单选框组件封装、文件上传组件封装、级联选择组件封装、富文本组件封装等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 ![在这里插入图…

无人机跟随一维高度避障场景--逻辑分析

无人机跟随一维高度避障场景--逻辑分析 1. 源由2. 视频3. 问题3.1 思维发散3.2 问题收敛 4. 图示4.1 水平模式4.2 下坡模式4.3 上坡模式4.4 碰撞分析 5. 总结5.1 一维高度避障场景5.2 业界跟随产品5.3 APM集成跟随示意图一&#xff1a;示意图二&#xff1a;示意图三&#xff1a…

Azure防火墙

文章目录 什么是Azure防火墙如何部署和配置创建虚拟网络创建虚拟机创建防火墙创建路由表&#xff0c;关联子网、路由配置防火墙策略配置应用程序规则配置网络规则配置 DNAT 规则 更改 Srv-Work 网络接口的主要和辅助 DNS 地址测试防火墙 什么是Azure防火墙 Azure防火墙是一种用…

XenDesktop5.6如何连接数据库

Citrix在数据库的连接方式上一直不统一&#xff0c;但是也还是有迹可循的。 经过了好长时间的下载以后&#xff0c;今天终于有时间来测试一下最新版本的XenDesktop 5 SP1&#xff0c;由于结合了其他组件和环境的需要&#xff0c;所以&#xff0c;选择了独立部署数据库&#xf…

clickhouse-监控配置

一、概述 监控是运维的一大利器&#xff0c;要想运维好clickhouse,首先就要对其进行监控&#xff0c;clickhouse有几种监控数据的方式&#xff0c;一种是系统本身监控&#xff0c;一种是通过exporter来监控&#xff0c;下面分别描述一下 二、系统自带监控 我下面会对监控做一…

Jenkins-发送邮件配置

在Jenkins构建执行完毕后&#xff0c;需要及时通知相关人员。因此在jenkins中是可以通过邮件通知的。 一、Jenkins自带的邮件通知功能 找到manage Jenkins->Configure System&#xff0c;进行邮件配置&#xff1a; 2. 配置Jenkins自带的邮箱信息 完成上面的配置后&#xf…

08 - 网络通信优化之IO模型:如何解决高并发下IO瓶颈?

提到 Java I/O&#xff0c;相信你一定不陌生。你可能使用 I/O 操作读写文件&#xff0c;也可能使用它实现 Socket 的信息传输…这些都是我们在系统中最常遇到的和 I/O 有关的操作。 我们都知道&#xff0c;I/O 的速度要比内存速度慢&#xff0c;尤其是在现在这个大数据时代背景…

排序算法-冒泡排序(C语言实现)

简介&#x1f600; 冒泡排序是一种简单但效率较低的排序算法。它重复地扫描待排序元素列表&#xff0c;比较相邻的两个元素&#xff0c;并将顺序错误的元素交换位置&#xff0c;直到整个列表排序完成。 实现&#x1f9d0; 以下内容为本人原创&#xff0c;经过自己整理得出&am…

银行客户关系管理系统springboot财务金融进销存java jsp源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 银行客户关系管理系统springboot 系统有1权限&#x…

基于蜉蝣算法优化的BP神经网络(预测应用) - 附代码

基于蜉蝣算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于蜉蝣算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.蜉蝣优化BP神经网络2.1 BP神经网络参数设置2.2 蜉蝣算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

互联网发展历程:保护与隔离,防火墙的安全壁垒

互联网的快速发展&#xff0c;不仅带来了便利和连接&#xff0c;也引发了越来越多的安全威胁。在数字时代&#xff0c;保护数据和网络安全变得尤为重要。然而&#xff0c;在早期的网络中&#xff0c;安全问题常常让人担忧。 安全问题的困扰&#xff1a;网络威胁日益增加 随着互…

透视俄乌网络战之一:数据擦除软件

数据擦除破坏 1. WhisperGate2. HermeticWiper3. IsaacWiper4. WhisperKill5. CaddyWiper6. DoubleZero7. AcidRain8. RURansom 数据是政府、社会和企业组织运行的关键要素。数据擦除软件可以在不留任何痕迹的情况下擦除数据并阻止操作系统恢复摧&#xff0c;达到摧毁或目标系统…

【数据分享】2013-2023年全国370个城市逐月空气质量数据(Excel格式/无需转发)

空气质量的好坏反映了空气污染程度&#xff0c;它是依据空气中污染物浓度的高低来判断的。在各项涉及城市环境的研究与实际项目中&#xff0c;城市空气质量都是一个十分重要的指标。那么&#xff0c;去哪里能获取到各城市空气质量的历史数据呢&#xff1f; 之前我们分享了2014…

KubeSphere 社区双周报 | Java functions framework 支持 SkyWalking | 2023.8.4-8.17

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.08.04-2023.…

【云原生】【k8s】Kubernetes+EFK构建日志分析安装部署

目录 EFK安装部署 一、环境准备&#xff08;所有主机&#xff09; 1、主机初始化配置 2、配置主机名并绑定hosts&#xff0c;不同主机名称不同 3、主机配置初始化 4、部署docker环境 二、部署kubernetes集群 1、组件介绍 2、配置阿里云yum源 3、安装kubelet kubeadm …

【淘宝】商品详情页+商品列表数据采集

作为国内最大的电商平台之一&#xff0c;淘宝数据采集具有多个维度。 有人需要采集商品信息&#xff0c;包括品类、品牌、产品名、价格、销量等字段&#xff0c;以了解商品销售状况、热门商品属性&#xff0c;进行市场扩大和重要决策&#xff1b; 有人需要采集产品评论&…

微人事 登录问题完善

重启服务端的时候&#xff0c;发现前端页面会操作不了&#xff0c;这样后端session会失效&#xff0c;我们就需要让页面重新跳转到登录页 springsecurity配置类后端配置 前端拦截器进行拦截跳转