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…...
基于Adafruit Trinket与旋转编码器制作USB物理音量旋钮
1. 项目概述与核心价值作为一个常年泡在电脑前,需要频繁切换音乐、会议和视频的开发者,我发现自己每天点击系统音量图标的次数多得离谱。那种在关键时刻需要快速调低音量,却不得不移动鼠标、寻找小图标的操作,不仅打断了工作流&am…...
自动同步总失败?NotebookLM本地缓存+云端快照双轨备份,手把手配置到上线仅需7分钟
更多请点击: https://intelliparadigm.com 第一章:NotebookLM数据备份方案 NotebookLM 是 Google 推出的基于用户上传文档进行 AI 助理问答的工具,但其本身不提供原生数据导出或持久化存储功能。为防止项目上下文丢失、模型重置或账户异常导…...
Win11触控板误触太烦人?三招精准关闭方案,总有一款适合你
1. 系统设置:最快捷的触控板关闭方案 刚换Win11那会儿,我总在打字时不小心碰到触控板,光标突然跳转导致输入错位。后来发现系统设置里藏着个"一键关闭"开关,实测下来这招最适合临时需要禁用触控板的场景。具体操作路径&…...
【麒麟系统-解释器错误:权限不足】
执行脚本后发现无法执行权限不足查看发现当前是有执行权限的;最后发现可能是有安全限制: 执行命令getstatus 执行这个命令即可:sudo setstatus softmode...
别只盯着密码爆破:身份认证漏洞的3个“非主流”攻击面与防御思考
身份认证安全的隐秘战场:超越密码爆破的三大高阶攻防实践 在网络安全领域,身份认证机制如同数字世界的门锁系统。当大多数安全从业者将注意力集中在传统的密码爆破防御时,攻击者早已将目光转向那些被忽视的认证薄弱环节。本文将深入剖析三个常…...
大语言模型在模块化布局优化中的应用与实战
1. 项目概述:当大语言模型遇见模块化布局优化在芯片设计和建筑规划领域,模块布局优化一直是个令人头疼的NP难问题。想象一下,你面前有16个形状各异的乐高积木(模块),需要将它们严丝合缝地拼成一个矩形底板&…...
基于R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析术应用
在自然和社会科学领域有大量与地理或空间有关的数据,这一类数据一般具有严重的空间异质性,而通常的统计学方法并不能处理空间异质性,因而对此类型的数据无能为力。以地理加权回归为基础的一系列方法:经典地理加权回归,…...
AI智能体编排框架Agent-Octo:章鱼架构解析与实战应用
1. 项目概述:当AI智能体遇上“章鱼”架构最近在开源社区里,一个名为purton-tech/agent-octo的项目引起了我的注意。乍一看这个标题,你可能会想,这又是一个AI智能体(Agent)框架。没错,它的核心确…...
2026学数据分析对就业能力提升的价值
一、行业需求与就业前景数据分析行业近年来的增长趋势和未来预测,2026年市场对数据分析师的需求量。不同行业(金融、医疗、电商等)对数据分析技能的具体需求。二、技能要求与学习路径数据分析岗位的核心技能(Python/R、SQL、统计学…...
Go语言交互式命令行工具开发:promptui库核心原理与实战应用
1. 项目概述:一个交互式命令行提示工具如果你经常在终端里写脚本,或者开发一些需要用户交互的命令行工具,那么对“如何优雅地获取用户输入”这个问题,一定深有感触。传统的read -p或者input()函数,功能单一、界面简陋&…...
