当前位置: 首页 > 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…...

算法分析与设计复习笔记

插入排序 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对象…...

一番赏小程序定制开发,打造全新抽赏体验平台

随着盲盒的热潮来袭&#xff0c;作为传统的潮玩方式一番赏也再次受到了大家的关注&#xff0c;市场热度不断上升&#xff01; 一番赏能够让玩家百分百中奖&#xff0c;商品种类丰富、收藏价值高&#xff0c;拥有各种IP&#xff0c;从而吸引着各个圈子的粉丝玩家&#xff0c;用…...

【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互

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

Oracle查询优化:高效实现仅查询前10条记录的方法与实践

在 Oracle 中&#xff0c;实现仅查询前10条记录的四种方法 1. 使用 ROWNUM 查询 ROWNUM 是 Oracle 中的伪列&#xff0c;用于限制返回的行数。 SELECT * FROM table_name WHERE condition AND ROWNUM < 10;condition&#xff1a;查询条件。ROWNUM < 10&#xff1a;限制…...

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 环境变量&#xff0c;以下三选一 七牛 CDN go env -w …...

mobi文件转成pdf

将 MOBI 文件转换为 PDF 格式通常涉及两个步骤&#xff1a; 解析 MOBI 文件&#xff1a;需要提取 MOBI 文件的内容&#xff08;文本、图片等&#xff09;。将提取的内容转换为 PDF&#xff1a;将 MOBI 文件的内容渲染到 PDF 格式。 可用工具 kindleunpack 或 mobi&#xff1…...

MobaXterm解决中文显示乱码问题

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

西门子 SINAMICS G120 变频器借助 ProfiNet 转 EtherCAT 实现与汇川 H5U 通讯实例

一&#xff0e; 案例背景 随着智能制造理念的推进&#xff0c;设备之间的协同工作变得越来越重要。例如&#xff0c;在机器人自动化焊接生产线中&#xff0c;电机驱动的焊接机器人需要与其他设备协同工作&#xff0c;这就要求负责电机控制的变频器和控制整个生产线流程的PLC能…...