c#-设计模式之观察者模式

news/2024/12/27 15:44:35/文章来源:https://www.cnblogs.com/mingcore/p/18523711

c#-设计模式之观察者模式

概念

指多个对象之间存在一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。该模式又称为包括发布-订阅(Publish/Subscribe)模式,该模式是行为型模式。

结构图

img

角色

  1. Subject(观察目标):观察目标是指被观察的对象,一般由抽象类或接口实现,观察目标把所有观察者对象的引用保存在一个列表中,并提供增加和删除观察者对象的操作,同时它定义了通知方法notify()。

  2. ConcreteSubject(具体观察目标):具体观察目标,实现观察目标,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。如果无须扩展目标类,则具体目标类可以省略。

  3. Observer(观察者):为所有具体观察者定义一个接口,观察者将对观察目标的改变做出反应。

  4. ConcreteObserver(具体观察者):实现观察目标所要求的接口,以便使自身状态与主题的状态相协调。

优点

  1. 降低了目标与观察者之间的耦合关系,观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。

  2. 观察者模式支持“广播通信”。主题会向所有的观察者发出通知。

  3. 观察者模式符合“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

缺点

  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

使用场景

  1. 当一个对象的改变需要同时改变其它对象时,而且不必知道具体有多少对象要改变。

  2. 完成某件事情后,异步通知的场景,例如用户注册(发短信、电子邮件)、用户登录(发送IM消息)。

代码实现

  1. 当股票购买者所购买的股票价格发生变化的时候,系统将自动发送通知给购买该股票的所有股民。

    此场景中,股民为观察者,股票为被观察者。

    观察者接口

    public interface IObserver
    {void Update(Stock stock);
    }
    

    具体观察者:股民

    public class Investor : IObserver
    {private string name;public Investor(string name){this.name = name;}public void Update(Stock stock){Console.WriteLine($"通知股民{name}: {stock.GetSymbol()}股票的价格变更为{stock.GetPrice()}");}
    }
    

    主题接口

    public interface ISubject
    {void RegisterObserver(IObserver observer);void RemoveObserver(IObserver observer);void NotifyObservers();
    }
    

    具体的主题:股票

    public class Stock : ISubject
    {  private List<IObserver> observers;private string symbol;private double price;public Stock(string symbol, double price){this.symbol = symbol;this.price = price;this.observers = new List<IObserver>();}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(this);}}public string GetSymbol(){return symbol;}public double GetPrice(){return price;}public void SetPrice(double price){this.price = price;NotifyObservers();}
    }
    

    客户端

     static void Main(string[] args){Stock stock = new Stock("A股", 120);Investor investor1 = new Investor("张三");Investor investor2 = new Investor("李四");stock.RegisterObserver(investor1);stock.RegisterObserver(investor2);stock.SetPrice(125);stock.SetPrice(115);stock.RemoveObserver(investor1);stock.SetPrice(130.0);Console.ReadLine();}
    

    运行截图

image

  1. 猫进屋了,老鼠都走了。

    此场景中,老鼠为观察者,猫为被观察者。

    public class Cat
    {private string color;private string name;public Cat(string color,string name){this.color = color;this.name = name;}/// <summary>/// 定义事件/// </summary>public event Action CatCome;public void CatComing(){Console.WriteLine(color + "的猫:" + name + "进屋了。");if(CatCome!=null){CatCome();}}
    }
    

​ 老鼠

public class Mouse
{private string color;private string name;public Mouse(string color, string name,Cat cat){this.color = color;this.name = name;cat.CatCome += this.Run;}public void Run(){Console.WriteLine(color + "的" + name + "说猫来了,赶紧跑!!!");}
}

客户端

static void Main(string[] args)
{Cat cat1 = new Cat("黄色", "加菲");Mouse mouse1 = new Mouse("黑色", "米奇", cat1);Mouse mouse2 = new Mouse("黑色", "杰克", cat1);Mouse mouse3 = new Mouse("黑色", "杰瑞", cat1);cat1.CatComing();
}

运行截图
image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/826253.html

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

相关文章

运动的时钟

#include <graphics.h> #include <conio.h> #include <math.h> #define Width 640 #define Height 480 #define PI 3.14159 int main() {// 初始化绘图窗口initgraph(640, 480, SHOWCONSOLE);//秒针起始坐标int center_x = Width / 2, center_y = Height / 2…

Python装饰器在接口测试中常用场景

日志记录 异常捕获与记录 解决Case间依赖 记录Case执行时间以记录Case执行时间为例:

20222401 2024-2025-1 《网络与系统攻防技术》实验四实验报告

1.实验内容 1.1基本概念 1.1.1什么是恶意代码 首先,恶意代码是一串实现特定功能的代码,而特定功能在这里特指具有恶意目的的功能实现,可以理解为按照攻击者的意愿进行运行的代码。 按照特征以及行为的不同,大概分为以下几类:计算机病毒 蠕虫(病毒) 后门 木马 僵尸网络 R…

目录提取,删除,添加

目录提取目录删除目录制作目录效果图 目前有两个问题,一般的目录文本格式有问题,需要用正则化方式,生成比较好的目录文件,比如csv格式,txt格式 另外,生成的目录,层级只有一级 提取目录import fitz # PyMuPDF import pandas as pd# 打开PDF文件 pdf_path = 力学概论.pdf…

2023 国际大学生程序设计竞赛亚洲区域赛(济南站)(SMU Autumn 2024 Team Round 2)

2023 国际大学生程序设计竞赛亚洲区域赛(济南站)(SMU Autumn 2024 Team Round 2) I. Strange Sorting 思路代码查看代码 #include #define ll __int128 #define int long long #define double long double #define PII pair using namespace std; const int N = 2E5 + 3; …

MyBatis-Plus快速入门:从安装到第一个Demo

本文将带你从零开始,快速入门 MyBatis-Plus。我们将首先介绍如何安装和配置 MyBatis-Plus,然后通过一个简单的示例演示如何使用它进行数据操作。无论你是 MyBatis 的新手还是希望提升开发效率的老手,本文都将为你提供清晰的指导和实用的技巧。一、前言 在现代 Java 应用程序…

docker bulid tag push到自己的docker hub 仓库

-t(或 --tag)参数:用于给构建的镜像指定标签(tag)。标签的格式通常是 [仓库名/][用户名/]镜像名:版本号 -f(或 --file)参数: 指定构建镜像所使用的 Dockerfile 的路径。默认情况下,docker build 会在当前目录下查找名为 Dockerfile 的文件,但通过这个参数可以指定其他…

《使用Gin框架构建分布式应用》阅读笔记:p393-p437

《用Gin框架构建分布式应用》学习第17天,p393-p437总结,总45页。 一、技术总结 1.Prometheus Prometheus放在代码里面使用,还是第一次见。在本人实际的工作中未看到这种用法。 2.Grafana Grafana用于被监控数据的可视化。 3.Telegraf Telegraf用于数据采集。在本人的实际工作…

2024.11.3 鲜花

浅谈 RMQ浅谈 RMQ비밀 인형극 II어느 한적한 마을 골목 안의 허름한 건물在某个僻静村庄胡同的破旧建筑里문을 열고 들어가면 작은 극장이 있죠开门进去便会见到一个小剧场솜씨 좋은 인형사가 연극을 마치고 떠나면一个手艺不错的人偶师演完戏离开的时候인형들은 극장 창고 안에…

2024-2025-1 20241301 《计算机基础与程序设计》第六周学习总结

|这个作业属于哪个课程|<2024-2025-1-计算机基础与程序设计(https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP)>| |这个作业要求在哪里|<2024-2025-1计算机基础与程序设计第六周作业](https://www.cnblogs.com/rocedu/p/9577842.html#WEEK06)>| |这个作业的…

【UWP】让 UWP 自己托管自己 —— Windows App SDK 篇

众所周知,UWP 使用的窗口模型是 CoreWindow,但是 UWP 本身只是一个应用模型,所以完全可以创建 win32 窗口,那么我们可以不可以创建一个 win32 窗口,然后像 XAML 岛 (XAML Islands) 一样把 XAML 托管上去呢?本篇将讲述如何利用 WAS (Windows App SDK,俗称 WinUI3) 在 UWP…

【算法】记忆化搜索

[!TIP] 一种剪枝算法,优化运算效率,减少冗余计算基本内容入门例子 [P1028 [NOIP2001 普及组] 数的计算]([P1028 NOIP2001 普及组] 数的计算 - 洛谷 | 计算机科学教育新生态)题目要求:输入n,输出一共可以构造多少个数列,要求数列的第 i不能超过第i-1个数的一半 示例:输入6…