C# 设计模式--观察者模式 (Observer Pattern)
定义
观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式的核心在于解耦主题(被观察者)和观察者之间的依赖关系。
正确写法
假设我们有一个天气预报系统,当天气变化时,需要通知多个订阅者(如手机应用、网站等)。
using System;
using System.Collections.Generic;// 被观察者接口
public interface ISubject
{void RegisterObserver(IObserver observer);void RemoveObserver(IObserver observer);void NotifyObservers();
}// 观察者接口
public interface IObserver
{void Update(string weatherInfo);
}// 具体被观察者
public class WeatherStation : ISubject
{private List<IObserver> observers = new List<IObserver>();private string weatherInfo;public void SetWeatherInfo(string info){this.weatherInfo = info;NotifyObservers();}public void RegisterObserver(IObserver observer){observers.Add(observer);}public void RemoveObserver(IObserver observer){observers.Remove(observer);}public void NotifyObservers(){foreach (var observer in observers){observer.Update(weatherInfo);}}
}// 具体观察者
public class MobileApp : IObserver
{public void Update(string weatherInfo){Console.WriteLine($"Mobile App received: {weatherInfo}");}
}public class Website : IObserver
{public void Update(string weatherInfo){Console.WriteLine($"Website received: {weatherInfo}");}
}// 客户端代码
class Program
{static void Main(string[] args){WeatherStation weatherStation = new WeatherStation();IObserver mobileApp = new MobileApp();IObserver website = new Website();weatherStation.RegisterObserver(mobileApp);weatherStation.RegisterObserver(website);weatherStation.SetWeatherInfo("Sunny and warm");weatherStation.RemoveObserver(mobileApp);weatherStation.SetWeatherInfo("Cloudy with a chance of rain");}
}
类图:
解释
- 被观察者接口 (
ISubject
):定义了注册、移除和通知观察者的方法。 - 观察者接口 (
IObserver
):定义了更新方法,当被观察者状态改变时,观察者会收到通知。 - 具体被观察者 (
WeatherStation
):实现了ISubject
接口,管理观察者的注册、移除和通知。 - 具体观察者 (
MobileApp
和Website
):实现了IObserver
接口,接收被观察者的通知并进行相应的处理。 - 客户端代码 (
Program
):创建了WeatherStation
实例,并注册了MobileApp
和Website
作为观察者。通过调用SetWeatherInfo
方法改变天气信息,触发观察者的更新方法。之后移除MobileApp
观察者,并再次改变天气信息,验证观察者机制。
用途
- 解耦主题和观察者:观察者模式使得主题和观察者之间的依赖关系变得松散,主题不需要知道观察者的具体实现。
- 动态注册和注销:观察者可以动态地注册和注销,使得系统更加灵活。
- 事件驱动:适用于事件驱动的系统,当某个事件发生时,可以通知多个观察者进行相应的处理。
优点
- 解耦:主题和观察者之间的依赖关系被解耦,使得代码更加灵活和可维护。
- 扩展性:增加新的观察者时,不需要修改主题的代码,符合开闭原则。
- 动态性:观察者可以动态地注册和注销,使得系统更加灵活。
缺点
- 性能问题:如果观察者数量较多,通知所有观察者可能会导致性能问题。
- 内存泄漏:如果观察者没有正确注销,可能会导致内存泄漏。
- 调试困难:由于观察者模式涉及多个对象之间的交互,调试时可能会比较困难。
适用场景
- 事件驱动系统:当需要在某个事件发生时通知多个对象时。
- 解耦对象:当需要解耦主题和观察者之间的依赖关系时。
- 动态注册和注销:当观察者需要动态地注册和注销时。
实际开发中的应用
1. 消息通知系统
假设我们正在开发一个消息通知系统,当用户发布新消息时,需要通知多个订阅者(如邮件、短信、推送通知等)。
using System;
using System.Collections.Generic;// 被观察者接口
public interface IMessagePublisher
{void RegisterSubscriber(IMessageSubscriber subscriber);void UnregisterSubscriber(IMessageSubscriber subscriber);void NotifySubscribers(string message);
}// 观察者接口
public interface IMessageSubscriber
{void OnMessageReceived(string message);
}// 具体被观察者
public class MessagePublisher : IMessagePublisher
{private List<IMessageSubscriber> subscribers = new List<IMessageSubscriber>();public void PublishMessage(string message){NotifySubscribers(message);}public void RegisterSubscriber(IMessageSubscriber subscriber){subscribers.Add(subscriber);}public void UnregisterSubscriber(IMessageSubscriber subscriber){subscribers.Remove(subscriber);}public void NotifySubscribers(string message){foreach (var subscriber in subscribers){subscriber.OnMessageReceived(message);}}
}// 具体观察者
public class EmailSubscriber : IMessageSubscriber
{public void OnMessageReceived(string message){Console.WriteLine($"Email received: {message}");}
}public class SMS Subscriber : IMessageSubscriber
{public void OnMessageReceived(string message){Console.WriteLine($"SMS received: {message}");}
}public class PushNotificationSubscriber : IMessageSubscriber
{public void OnMessageReceived(string message){Console.WriteLine($"Push notification received: {message}");}
}// 客户端代码
class Program
{static void Main(string[] args){MessagePublisher publisher = new MessagePublisher();IMessageSubscriber emailSubscriber = new EmailSubscriber();IMessageSubscriber smsSubscriber = new SMS Subscriber();IMessageSubscriber pushNotificationSubscriber = new PushNotificationSubscriber();publisher.RegisterSubscriber(emailSubscriber);publisher.RegisterSubscriber(smsSubscriber);publisher.RegisterSubscriber(pushNotificationSubscriber);publisher.PublishMessage("New message: Hello, world!");publisher.UnregisterSubscriber(emailSubscriber);publisher.PublishMessage("Another message: Goodbye, world!");}
}
2. 股票市场监控
假设我们正在开发一个股票市场监控系统,当股票价格发生变化时,需要通知多个订阅者(如投资者、分析师等)。
```csharp
using System;
using System.Collections.Generic;// 被观察者接口
public interface IStockMarket
{void RegisterInvestor(IInvestor investor);void UnregisterInvestor(IInvestor investor);void NotifyInvestors(string stockInfo);
}// 观察者接口
public interface IInvestor
{void OnStockPriceChange(string stockInfo);
}// 具体被观察者
public class StockMarket : IStockMarket
{private List<IInvestor> investors = new List<IInvestor>();public void UpdateStockPrice(string stockInfo){NotifyInvestors(stockInfo);}public void RegisterInvestor(IInvestor investor){investors.Add(investor);}public void UnregisterInvestor(IInvestor investor){investors.Remove(investor);}public void NotifyInvestors(string stockInfo){foreach (var investor in investors){investor.OnStockPriceChange(stockInfo);}}
}// 具体观察者
public class IndividualInvestor : IInvestor
{public void OnStockPriceChange(string stockInfo){Console.WriteLine($"Individual Investor received: {stockInfo}");}
}public class InstitutionalInvestor : IInvestor
{public void OnStockPriceChange(string stockInfo){Console.WriteLine($"Institutional Investor received: {stockInfo}");}
}public class Analyst : IInvestor
{public void OnStockPriceChange(string stockInfo){Console.WriteLine($"Analyst received: {stockInfo}");}
}// 客户端代码
class Program
{static void Main(string[] args){StockMarket market = new StockMarket();IInvestor individualInvestor = new IndividualInvestor();IInvestor institutionalInvestor = new InstitutionalInvestor();IInvestor analyst = new Analyst();market.RegisterInvestor(individualInvestor);market.RegisterInvestor(institutionalInvestor);market.RegisterInvestor(analyst);market.UpdateStockPrice("Apple stock price increased by 5%");market.UnregisterInvestor(individualInvestor);market.UpdateStockPrice("Google stock price decreased by 2%");}
}
总结
观察者模式通过定义对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。虽然它可能会带来一些性能和调试上的问题,但在需要解耦主题和观察者、支持动态注册和注销的场景中,观察者模式能够显著提高代码的可维护性和扩展性。
相关文章:

C# 设计模式--观察者模式 (Observer Pattern)
定义 观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式的核心在于解耦主题(被观察者)和观察者之间的依赖关系。 …...

【开发语言】层次状态机(HSM)介绍
层次状态机(Hierarchical State Machine, HSM),从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。 1. 层次状态机的基本原理 层次状态机是一种用于管理复杂系统行为的状态机模型,它通过将状态组织成…...

03-13、SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel
SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel 一、Sentinel概述 1、Sentinel是什么 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保…...

【k8s 深入学习之 event 聚合】event count累记聚合(采用 Patch),Message 聚合形成聚合 event(采用Create)
参考 15.深入k8s:Event事件处理及其源码分析 - luozhiyun - 博客园event 模块总览 EventRecorder:是事件生成者,k8s组件通过调用它的方法来生成事件;EventBroadcaster:事件广播器,负责消费EventRecorder产生的事件,然后分发给broadcasterWatcher;broadcasterWatcher:用…...

leetcode104.二叉树的最大深度
给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3示例 2: 输入:root [1,null,2] 输出…...

蓝桥杯2117砍竹子(简单易懂 包看包会版)
问题描述 这天, 小明在砍竹子, 他面前有 n 棵竹子排成一排, 一开始第 i 棵竹子的 高度为 hi. 他觉得一棵一棵砍太慢了, 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用, 假设这一段竹子的高度为 H, 那么 用一次魔法可以 把这一段竹子的高度都变为 ⌊H2⌋…...

LCD与lvgl
LCD与lvgl 目录 LCD与lvgl 回顾 LCD 的驱动层讲解 1、LCD 的常见接口 2、我们的 LCD 的参数 3、LCD 的设备树说明 4、LCD 的设备树说明 5、如何移植 LCD 的驱动(重点) LCD 的应用层开发 1:LCD 应用开发->界面开发的方法 2:LVGL 模拟器安装 3:LVGL 工程创建和…...

SpringBoot 赋能:精铸超稳会员制医疗预约系统,夯实就医数据根基
1绪论 1.1开发背景 传统的管理方式都在使用手工记录的方式进行记录,这种方式耗时,而且对于信息量比较大的情况想要快速查找某一信息非常慢,对于会员制医疗预约服务信息的统计获取比较繁琐,随着网络技术的发展,采用电脑…...

android studio 读写文件操作(应用场景二)
android studio版本:2023.3.1 patch2 例程:readtextviewIDsaveandread 本例程是个过渡例程,如果单是实现下图的目的有更简单的方法,但这个方法是下一步工作的基础,所以一定要做。 例程功能:将两个textvi…...

小尺寸低功耗蓝牙模块在光伏清扫机器人上的应用
一、引言 随着可再生能源的迅速发展,光伏发电系统的清洁与维护变得越来越重要。光伏清扫机器人通过自动化技术提高了清洁效率,而蓝牙模组的集成为这些设备提供了更为智能的管理和控制方案。 二、蓝牙模组的功能与实现: 蓝牙模组ANS-BT103M…...

防火墙有什么作用
防火墙的作用:1. 提供网络安全防护;2. 实施访问控制和流量过滤;3. 检测和阻止恶意攻击;4. 保护内部网络免受未经授权的访问;5. 监控网络流量和安全事件;6. 支持虚拟专用网络(VPN)。防…...

MongoDB-BSON 协议与类型
前言: MongoDB 是一个高性能、无模式的 NoSQL 数据库,广泛应用于大数据处理和实时数据存储。作为一个数据库系统,MongoDB 的核心之一就是其使用的 BSON(Binary JSON)格式,它用于存储数据以及在客户端和数据…...

学习threejs,使用VideoTexture实现视频Video更新纹理
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️VideoTexture 视频纹理 二、…...

怎么获取键值对的键的数值?
问: 通过paelData.cardMap.C0002112可以获取到Cooo2112里面的数据,但是有时候接口返回的不是C0002112而是C0002093或者其他值,请问我该怎么写? 后端返回的数据是这样的: cardMap: { C0002112: { name: Item 1, va…...

数据结构排序算法详解
数据结构排序算法详解 1、冒泡排序(Bubble Sort)2、选择排序(Selection Sort)2、插入排序(Insertion Sort) 1、冒泡排序(Bubble Sort) 原理:越小的元素会慢慢“浮”到数…...

在Linux设置postgresql开机自启动,创建一个文件 postgresql-15.service
在Linux设置postgresql开机自启动,创建一个文件 postgresql-15.service 在Linux设置postgresql开机自启动,创建一个文件 postgresql-15.service1. 创建 systemd 服务文件2. 编辑服务文件3. 保存并退出4. 重新加载 systemd 配置5. 启动 PostgreSQL 服务6.…...

【kafka】消息队列的认识,Kafka与RabbitMQ的简单对比
什么是消息队列? 消息队列(Message Queue,简称 MQ)是一个在不同应用程序、系统或服务之间传递数据的机制。 它允许系统间异步地交换信息,而无需直接交互,确保消息的可靠传输。 想象一下,你正在…...

ProjectSend 身份认证绕过漏洞复现(CVE-2024-11680)
0x01 产品描述: ProjectSend 是一个开源文件共享网络应用程序,旨在促进服务器管理员和客户端之间的安全、私密文件传输。它是一款相当流行的应用程序,被更喜欢自托管解决方案而不是 Google Drive 和 Dropbox 等第三方服务的组织使用。0x02 漏洞描述: ProjectSend r1720 之前…...

Android笔记(三十四):onCreate执行Handler.post在onResume后才能执行?
背景 偶然发现一个点,就是在onCreate执行Handler.post在onResume后才执行,以下是测试代码 多次运行的结果一致,为什么execute runnable不是在onCreate和onResume之间执行的呢,带着疑问撸了一遍Activity启动流程 关键源码分析 …...

关闭模组的IP过滤功能
关闭模组的IP过滤功能 关闭模组的IP过滤功能 本脚本用于关闭模组的IP过滤功能,关闭后, 源地址不是终端IP的数据包,也可以被模组转发给网络 关闭模组的IP过滤功能 cat > /usr/bin/ipfilter << "EOF"echo -e "ATCFUN…...

算法分析与设计复习笔记
插入排序 1. void insert_sort(int A[ ],int n) 2. { 3. int a,i,j; 4. for (i1;i<n;i) { 5. a A[ i ]; 6. j i – 1; 7. while (j>0 && A[j]>a) { 8. A[ j…...

vue-amap 高德地图
vue-amap是一套基于Vue 2/vue3和高德地图的地图组件 vue-amap 高德地图2.0版本的对应vue3...

Numpy基础练习
import numpy as np 1.创建一个长度为10的一维全为0的ndarray对象,然后让第5个元素等于1 n np.zeros(10,dtypenp.int32) n[4] 12.创建一个元素从10到49的ndarray对象 n np.arrange(10,50)3.将第2题的所有元素位置反转 n[::-1]使用np.random.random创建一个10*10的ndarray对象…...

一番赏小程序定制开发,打造全新抽赏体验平台
随着盲盒的热潮来袭,作为传统的潮玩方式一番赏也再次受到了大家的关注,市场热度不断上升! 一番赏能够让玩家百分百中奖,商品种类丰富、收藏价值高,拥有各种IP,从而吸引着各个圈子的粉丝玩家,用…...

【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互
【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互 <template><div><el-button click"start">调用方法</el-button></div> </template> <script> // import { JScallbackProc } from ./JScal…...

Oracle查询优化:高效实现仅查询前10条记录的方法与实践
在 Oracle 中,实现仅查询前10条记录的四种方法 1. 使用 ROWNUM 查询 ROWNUM 是 Oracle 中的伪列,用于限制返回的行数。 SELECT * FROM table_name WHERE condition AND ROWNUM < 10;condition:查询条件。ROWNUM < 10:限制…...

go语言编译问题
go编译 goproxy地址 阿里云 https://mirrors.aliyun.com/goproxy/七牛云 https://goproxy.cn/开源版 https://goproxy.io/nexus社区 https://gonexus.dev/启用 Go Modules 功能 go env -w GO111MODULEon配置 GOPROXY 环境变量,以下三选一 七牛 CDN go env -w …...

mobi文件转成pdf
将 MOBI 文件转换为 PDF 格式通常涉及两个步骤: 解析 MOBI 文件:需要提取 MOBI 文件的内容(文本、图片等)。将提取的内容转换为 PDF:将 MOBI 文件的内容渲染到 PDF 格式。 可用工具 kindleunpack 或 mobi࿱…...

MobaXterm解决中文显示乱码问题
1 问题 打开MobaXterm时,会显示中文乱码。 2 解决方法 右键点击会话,在弹出菜单中选择“编辑会话”,如下: 选择终端字体设置,如下: 字符集换成ISO-8859-1,如下: 网上有说用…...

西门子 SINAMICS G120 变频器借助 ProfiNet 转 EtherCAT 实现与汇川 H5U 通讯实例
一. 案例背景 随着智能制造理念的推进,设备之间的协同工作变得越来越重要。例如,在机器人自动化焊接生产线中,电机驱动的焊接机器人需要与其他设备协同工作,这就要求负责电机控制的变频器和控制整个生产线流程的PLC能…...