当前位置: 首页 > news >正文

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
+void RegisterObserver(IObserver observer)
+void RemoveObserver(IObserver observer)
+void NotifyObservers()
IObserver
+void Update(string weatherInfo)
WeatherStation
-List observers
-string weatherInfo
+void SetWeatherInfo(string info)
+void RegisterObserver(IObserver observer)
+void RemoveObserver(IObserver observer)
+void NotifyObservers()
MobileApp
+void Update(string weatherInfo)
Website
+void Update(string weatherInfo)
Program
+static void Main(string[] args)

解释

  1. 被观察者接口 (ISubject):定义了注册、移除和通知观察者的方法。
  2. 观察者接口 (IObserver):定义了更新方法,当被观察者状态改变时,观察者会收到通知。
  3. 具体被观察者 (WeatherStation):实现了 ISubject 接口,管理观察者的注册、移除和通知。
  4. 具体观察者 (MobileAppWebsite):实现了 IObserver 接口,接收被观察者的通知并进行相应的处理。
  5. 客户端代码 (Program):创建了 WeatherStation 实例,并注册了 MobileAppWebsite 作为观察者。通过调用 SetWeatherInfo 方法改变天气信息,触发观察者的更新方法。之后移除 MobileApp 观察者,并再次改变天气信息,验证观察者机制。

用途

  1. 解耦主题和观察者:观察者模式使得主题和观察者之间的依赖关系变得松散,主题不需要知道观察者的具体实现。
  2. 动态注册和注销:观察者可以动态地注册和注销,使得系统更加灵活。
  3. 事件驱动:适用于事件驱动的系统,当某个事件发生时,可以通知多个观察者进行相应的处理。

优点

  1. 解耦:主题和观察者之间的依赖关系被解耦,使得代码更加灵活和可维护。
  2. 扩展性:增加新的观察者时,不需要修改主题的代码,符合开闭原则。
  3. 动态性:观察者可以动态地注册和注销,使得系统更加灵活。

缺点

  1. 性能问题:如果观察者数量较多,通知所有观察者可能会导致性能问题。
  2. 内存泄漏:如果观察者没有正确注销,可能会导致内存泄漏。
  3. 调试困难:由于观察者模式涉及多个对象之间的交互,调试时可能会比较困难。

适用场景

  1. 事件驱动系统:当需要在某个事件发生时通知多个对象时。
  2. 解耦对象:当需要解耦主题和观察者之间的依赖关系时。
  3. 动态注册和注销:当观察者需要动态地注册和注销时。
    实际开发中的应用

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)

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

【开发语言】层次状态机(HSM)介绍

层次状态机&#xff08;Hierarchical State Machine, HSM&#xff09;&#xff0c;从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。 1. 层次状态机的基本原理 层次状态机是一种用于管理复杂系统行为的状态机模型&#xff0c;它通过将状态组织成…...

03-13、SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel

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

【k8s 深入学习之 event 聚合】event count累记聚合(采用 Patch),Message 聚合形成聚合 event(采用Create)

参考 15.深入k8s:Event事件处理及其源码分析 - luozhiyun - 博客园event 模块总览 EventRecorder:是事件生成者,k8s组件通过调用它的方法来生成事件;EventBroadcaster:事件广播器,负责消费EventRecorder产生的事件,然后分发给broadcasterWatcher;broadcasterWatcher:用…...

leetcode104.二叉树的最大深度

给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;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开发背景 传统的管理方式都在使用手工记录的方式进行记录&#xff0c;这种方式耗时&#xff0c;而且对于信息量比较大的情况想要快速查找某一信息非常慢&#xff0c;对于会员制医疗预约服务信息的统计获取比较繁琐&#xff0c;随着网络技术的发展&#xff0c;采用电脑…...

android studio 读写文件操作(应用场景二)

android studio版本&#xff1a;2023.3.1 patch2 例程&#xff1a;readtextviewIDsaveandread 本例程是个过渡例程&#xff0c;如果单是实现下图的目的有更简单的方法&#xff0c;但这个方法是下一步工作的基础&#xff0c;所以一定要做。 例程功能&#xff1a;将两个textvi…...

小尺寸低功耗蓝牙模块在光伏清扫机器人上的应用

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

防火墙有什么作用

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

MongoDB-BSON 协议与类型

前言&#xff1a; MongoDB 是一个高性能、无模式的 NoSQL 数据库&#xff0c;广泛应用于大数据处理和实时数据存储。作为一个数据库系统&#xff0c;MongoDB 的核心之一就是其使用的 BSON&#xff08;Binary JSON&#xff09;格式&#xff0c;它用于存储数据以及在客户端和数据…...

学习threejs,使用VideoTexture实现视频Video更新纹理

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️VideoTexture 视频纹理 二、…...

怎么获取键值对的键的数值?

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

数据结构排序算法详解

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

在Linux设置postgresql开机自启动,创建一个文件 postgresql-15.service

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

【kafka】消息队列的认识,Kafka与RabbitMQ的简单对比

什么是消息队列&#xff1f; 消息队列&#xff08;Message Queue&#xff0c;简称 MQ&#xff09;是一个在不同应用程序、系统或服务之间传递数据的机制。 它允许系统间异步地交换信息&#xff0c;而无需直接交互&#xff0c;确保消息的可靠传输。 想象一下&#xff0c;你正在…...

ProjectSend 身份认证绕过漏洞复现(CVE-2024-11680)

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

Android笔记(三十四):onCreate执行Handler.post在onResume后才能执行?

背景 偶然发现一个点&#xff0c;就是在onCreate执行Handler.post在onResume后才执行&#xff0c;以下是测试代码 多次运行的结果一致&#xff0c;为什么execute runnable不是在onCreate和onResume之间执行的呢&#xff0c;带着疑问撸了一遍Activity启动流程 关键源码分析 …...

关闭模组的IP过滤功能

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

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...