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.mzph.cn/news/44777.shtml

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

相关文章

Ribbon负载均衡

Ribbon与Eureka的关系 Eureka的服务拉取与负载均衡都是由Ribbon来实现的。 当服务发送http://userservice/user/xxxhtt://userservice/user/xxx请求时&#xff0c;是无法到达userservice服务的&#xff0c;会通过Ribbon会把这个请求拦截下来&#xff0c;通过Eureka-server转换…

常见排序集锦-C语言实现数据结构

目录 排序的概念 常见排序集锦 1.直接插入排序 2.希尔排序 3.选择排序 4.堆排序 5.冒泡排序 6.快速排序 hoare 挖坑法 前后指针法 非递归 7.归并排序 非递归 排序实现接口 算法复杂度与稳定性分析 排序的概念 排序 &#xff1a;所谓排序&#xff0c;就是使一串记录&#…

排名前 6 位的数学编程语言

0 说明 任何对数学感兴趣或计划学习数学的人&#xff0c;都应该至少对编程语言有一定的流利程度。您不仅会更有就业能力&#xff0c;还可以更深入地理解和探索数学。那么你应该学习什么语言呢&#xff1f; 1.python 对于任何正在学习数学的人来说&#xff0c;Python都是一门很棒…

【Linux从入门到精通】动静态库的原理与制作详解

本篇文章主要是围绕动静态库的原理与制作进行展开讲解的。其中涉及到了inode的概念引入和软硬连接的讲解。会结合实际操作对这些抽象的概念进行解释&#xff0c;希望会对你有所帮助。 文章目录 一、inode 概念 二、软硬链接 2、1 软连接 2、2 硬链接 三、动静态库概念 3、1 静态…

编织梦想:SpringBoot AOP 教程与自定义日志切面完整实战

什么是 AOP AOP 是指通过预编译方式和运行期动态代理的方式&#xff0c;在不修改源代码的情况下对程序进行功能增强的一种技术。AOP 不是面向对象编程&#xff08;OOP&#xff09;的替代品&#xff0c;而是 OOP 的补充和扩展。它是一个新的维度&#xff0c;用来表达横切问题&a…

常见前端基础面试题(HTML,CSS,JS)(三)

JS 中如何进行数据类型的转换&#xff1f; 类型转换可以分为两种&#xff0c;隐性转换和显性转换 显性转换 主要分为三大类&#xff1a;数值类型、字符串类型、布尔类型 三大类的原始类型值的转换规则我就不一一列举了 数值类型&#xff08;引用类型转换&#xff09; Numbe…

设计模式之状态模式(State)的C++实现

1、状态模式的提出 在组件功能开发过程中&#xff0c;某些对象的状态经常面临变化&#xff0c;不同的状态&#xff0c;其对象的操作行为不同。比如根据状态写的if else条件情况&#xff0c;且这种条件变化是经常变化的&#xff0c;这样的代码不易维护。可以使用状态模式解决这…

如何在window下cmd窗口执行linux指令?

1.Git&#xff1a;https://git-scm.com/downloads(官网地址) 2.根据自己的实际路径,添加两个环境变量 3.重启电脑

删除有序链表中重复的元素-II(链表)

乌&#xff01;蒙&#xff01;山&#xff01;连&#xff01;着&#xff01;山&#xff01;外&#xff01;山&#xff01; 题目&#xff1a; 思路&#xff1a; 双指针&#xff0c;slow和fast&#xff0c;并且增加标记flag初始为1。 如果slow指向节点值等于fast指向节点值&…

Servlet 初步学习

文章目录 Servlet1 简介2 快速入门3 执行流程4 生命周期5 方法介绍6 体系结构7 urlPattern配置8 XML配置 Servlet 1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门 动态 web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面…

什么是cURL?

cURL无处不在。它几乎隐藏在所有设备中&#xff0c;例如汽车&#xff0c;蓝光播放器等。它通过互联网协议传输任意类型数据。 在本文中&#xff0c;我们将揭开cURL神秘命令行工具的面纱&#xff0c;解释它是如何成为一种通用代码的&#xff0c;并举例说明其用法。 cURL是什么意…

PHP海外代购管理系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 海外代购管理系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 代码下载 https://download.csdn.net/download/qq_41221322/88229435 论文 https://…

Redis中的分布式锁及其延生的问题

前言 本文将着重介绍Redis中的分布式锁及其与出现的死锁和锁误删问题 什么是分布式锁 首先问题就是什么是分布式锁&#xff0c;分布式锁就是分布式系统中实现并发控制的一种锁机制&#xff0c;它可以保证多个节点在同一个时间只有有一个能成功竞争到系统资源&#xff08;共享…

LeetCode算法递归类—二叉树中的最大路径和

目录 124. 二叉树中的最大路径和 - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a; 代码&#xff1a; 运行结果&#xff1a; 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该…

基于Opencv的虚拟拖拽项目

预备知识 勾股定理 跟随移动算法 手势识别图解 项目源代码 """ 演示一个简单的虚拟拖拽 步骤&#xff1a; 1、opencv 读取视频流 2、在视频图像上画一个方块 3、通过mediapipe库获取手指关节坐标 4、判断手指是否在方块上 5、是&#xff0c;方块跟着移动 6、…

密码学学习笔记(十九):密码学关键术语的解释1

数据加密标准(DES) 数据加密标准是使用最广泛的加密体制&#xff0c;它于1977年被美国国家标准和技术研究所(NIST)采纳为联邦信息处理标准FIPS PUB 46。 DES3DESAES明文分组长度&#xff08;位&#xff09;6464128密文分组长度&#xff08;位&#xff09;6464128密钥长度&…

详解junit

目录 1.概述 2.断言 3.常用注解 3.1.Test 3.2.Before 3.3.After 3.4.BeforeClass 3.5.AfterClass 4.异常测试 5.超时测试 6.参数化测试 1.概述 什么是单元测试&#xff1a; 单元测试&#xff0c;是针对最小的功能单元编写测试代码&#xff0c;在JAVA中最小的功能单…

0101读写分离测试-jdbc-shardingsphere-中间件

文章目录 1 前言2、创建SpringBoot程序2.1、创建项目2.2、添加依赖2.3、生成实体类、service与Mapper1.5、配置读写分离 2、测试2.1、读写分离测试2.2、事务测试2.3、负载均衡测试 结语 1 前言 shardingshpere-jdbc定位为轻量级 Java 框架&#xff0c;在 Java 的 JDBC 层提供的…

【前端面试】中大文件上传/下载:中等文件代理服务器放行+大文件切片传输+并发请求+localstorage实现断点续传

目录 中等文件代理服务器放行&#xff1a;10MB为单位 proxy nginx 大文件切片&#xff1a;100MB为单位 断点&#xff1a;存储切片hash 前端方案A localstorage 后端方案B 服务端 上传 前端 后端 下载 前端 后端 多个大文件传输&#xff1a;spark-md5 哈希碰撞…

什么是Pytorch?

当谈及深度学习框架时&#xff0c;PyTorch 是当今备受欢迎的选择之一。作为一个开源的机器学习库&#xff0c;PyTorch 为研究人员和开发者们提供了一个强大的工具来构建、训练以及部署各种深度学习模型。你可能会问&#xff0c;PyTorch 是什么&#xff0c;它有什么特点&#xf…