C#设计模式学习笔记:(16)观察者模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7928521.html,记录一下学习过程以备后续查用。
一、引言
今天我们要讲行为型设计模式的第四个模式--观察者模式,先从名字上来看。观察者模式可以理解为既然有“观察者”,那肯定就有“被观察者”了。“观察者”
监视着“被观察者”,如果“被观察者”有所行动,“观察者”就会做出相应的动作来回应。听起来是不是有点像“谍战”的味道?比如“谍影重重”那类优秀的影片。
观察者模式在现实生活中,实例其实是很多的,比如:八九十年代我们订阅的报纸,我们会定期收到报纸,因为我们订阅了。银行可以给储户发手机短信,
也是观察者模式很好的使用的例子,因为我们订阅了银行的短信业务,当我们账户余额发生变化就会收到通知。还有很多,我就不一一列举了,发挥大家的
想象吧。好了,接下来,就让我们看看该模式具体是怎么实现的吧。
二、观察者模式介绍
观察者模式:英文名称--Observer Pattern;分类--行为型。
2.1、动机(Motivate)
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”--一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。
如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系,从而实现软件体
系结构的松耦合。
2.2、意图(Intent)
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。——《设计模式》GoF
2.3、结构图

2.4、模式的组成
可以看出,在观察者模式的结构图有以下角色:
1)抽象主题角色(Subject):抽象主题把所有观察者对象的引用保存在一个列表中,并提供增加和删除观察者对象的操作,抽象主题角色又叫做抽象被观
察者角色,一般由抽象类或接口实现。
2)抽象观察者角色(Observer):为所有具体观察者定义一个接口,在得到主题通知时更新自己,一般由抽象类或接口实现。
3)具体主题角色(ConcreteSubject):实现抽象主题接口,具体主题角色又叫做具体被观察者角色。
4)具体观察者角色(ConcreteObserver):实现抽象观察者角色所要求的接口,以便使自身状态与主题的状态相协调。
2.5、观察者模式的具体实现
观察者模式在显示生活中也有类似的例子,比如:我们订阅银行短信业务,当我们账户发生改变,我们就会收到相应的短信。类似的还有微信订阅号,今天
我们以订阅银行短信业务为例来讲讲观察者模式的实现,实现代码如下:
class Program
{
/// <summary>
/// 银行短信系统抽象接口,是被观察者--该类型相当于抽象主题角色Subject。
/// </summary>
public abstract class BankMessageSystem
{
protected IList<Depositor> observers; //构造函数初始化观察者列表实例
protected BankMessageSystem()
{
observers = new List<Depositor>();
} //增加预约储户
public abstract void Add(Depositor depositor); //删除预约储户
public abstract void Delete(Depositor depositor); //通知储户
public void Notify()
{
foreach (Depositor depositor in observers)
{
if (depositor.AccountIsChanged)
{
depositor.Update(depositor.Balance, depositor.OperationDateTime);
//账户发生变化并且通知后,则可认为账户已无变化。
depositor.AccountIsChanged = false;
}
}
}
} /// <summary>
/// 广东银行短信系统,是被观察者--该类型相当于具体主题角色ConcreteSubject。
/// </summary>
public sealed class GuangDongBankMessageSystem : BankMessageSystem
{
//增加预约储户
public override void Add(Depositor depositor)
{
//应该先判断该用户是否已预约?如未预约则增加到列表中,这里简化了。
observers.Add(depositor);
} //删除预约储户
public override void Delete(Depositor depositor)
{
//应该先判断该用户是否有预约?如有则删除,否则不操作,这里简化了。
observers.Remove(depositor);
}
} /// <summary>
/// 储户的抽象接口--相当于抽象观察者角色(Observer)
/// </summary>
public abstract class Depositor
{
//储户的名称,假设是唯一的。
public string Name { get; private set; } //储户的余额
public int Balance { get; private set; } //账户操作时间
public DateTime OperationDateTime { get; set; } //账户是否发生变化
public bool AccountIsChanged { get; set; } //初始化状态数据
protected Depositor(string name, int total)
{
Name = name;
Balance = total; //存款总额等于余额
AccountIsChanged = false; //账户未发生变化
} //取钱
public void GetMoney(int num)
{
if (num <= Balance && num > )
{
Balance -= num;
AccountIsChanged = true;
OperationDateTime = DateTime.Now;
}
} //更新储户状态
public abstract void Update(int currentBalance, DateTime dateTime);
} /// <summary>
/// 广东的具体储户--相当于具体观察者角色ConcreteObserver
/// </summary>
public sealed class GuangDongDepositor : Depositor
{
public GuangDongDepositor(string name, int total) : base(name, total) { } public override void Update(int currentBalance, DateTime dateTime)
{
Console.WriteLine(string.Format(Name + ",您的账户余额有变化,发生时间:{0},当前余额:{1}元。", dateTime.ToString(), currentBalance.ToString()));
}
} static void Main(string[] args)
{
#region 观察者模式
//假设有3位储户,都是武林高手,也比较有钱。
Depositor huangFeiHong = new GuangDongDepositor("黄飞鸿", );
Depositor fangShiYu = new GuangDongDepositor("方世玉", );
Depositor hongXiGuan = new GuangDongDepositor("洪熙官", );
BankMessageSystem guangDongBank = new GuangDongBankMessageSystem(); //这三位开始订阅银行短信业务
guangDongBank.Add(huangFeiHong);
guangDongBank.Add(fangShiYu);
guangDongBank.Add(hongXiGuan); //早上黄飞鸿取了100块钱
huangFeiHong.GetMoney();
guangDongBank.Notify(); //中午黄飞鸿和方世玉各取了200块
huangFeiHong.GetMoney();
fangShiYu.GetMoney();
guangDongBank.Notify(); //晚上他们三个都取了钱
huangFeiHong.GetMoney();
fangShiYu.GetMoney();
hongXiGuan.GetMoney();
guangDongBank.Notify(); Console.Read();
#endregion
}
}
运行结果如下:

观察者模式有些麻烦的地方就是关于状态的处理,大家可以细细体会一下。模式还是要多写多练习,里面的道理就不难理解了。
三、观察者模式的实现要点
使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者(面向对象中的改变不是指改代码,而是指扩展、子类化、实现接口),从而使
二者之间的依赖关系达到松耦合。目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知,
目标对象对此一无所知。
在C#的event中,委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象。委托是比抽象Observer接口更为松耦合的设计。
3.1、观察者模式的优点
1)观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,同时抽象了更新接口,使得可以有各种各样不同的表示层,即观察者。
2)观察者模式在被观察者和观察者之间建立了一个抽象的耦合,被观察者并不知道任何一个具体的观察者,只是保存着抽象观察者的列表,每个具体观察者
都符合一个抽象观察者的接口。
3)观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知。
3.2、观察者模式的缺点
1)如果一个被观察者有很多直接和间接的观察者时,将所有的观察者都通知到会花费很多时间。
2)虽然观察者模式可以随时使观察者知道所观察的对象发送了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎样发生变化的。
3)如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃,在使用观察者模式应特别注意这点。
四、.NET中观察者模式的实现
其实在Net里面实现的观察者模式做了一些改变,用委托或者说是事件来实现观察者模式。事件我们都很明白,可以注册控件的事件,当触发控件的动作时候,
相应的事件就会执行,在事件的执行过程中我们就可以做相关的提醒业务。这里关于观察者模式在Net里面的实现就不说了,如果大家不明白,可以多看看相关委
托或者事件的相关资料。
五、总结
这个模式结合实例理解是很容易的,模式的使用我们不能照搬,要理解,当然多多联系和写代码也是必不可少的,我们使用模式的一贯宗旨是通过重构和迭代,
在我们的代码中实现相应的模式。
C#设计模式学习笔记:(16)观察者模式的更多相关文章
- Java设计模式学习笔记(观察者模式)
观察者模式说起来很简单,就是一个订报纸的模式.但是实际上这部分我觉得还是很有意思的,<Head First设计模式>里还有一些还没看完,也是因为理解的不够深吧. 观察者模式会包含两个组件: ...
- 设计模式学习笔记——Observer观察者模式
观察者模式里面有两个东西:观察者(Observer)和目标(Subject).当目标发生变化的时候,观察者随之起舞,也作出相应的变化.此为观察者模式. 这是怎么做到的?主要是目标里面存有一份观察者的名 ...
- Java设计模式学习笔记(二) 简单工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...
- Java设计模式学习笔记(四) 抽象工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...
- Java设计模式学习笔记(五) 单例模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 使用单例模式的原因 以Windows任务管理器为例,在Windows系统中,任务管理器是唯 ...
- 7 种 Javascript 常用设计模式学习笔记
7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...
- Ext.Net学习笔记16:Ext.Net GridPanel 折叠/展开行
Ext.Net学习笔记16:Ext.Net GridPanel 折叠/展开行 Ext.Net GridPanel的行支持折叠/展开功能,这个功能个人觉得还说很有用处的,尤其是数据中包含图片等内容的时候 ...
- SQL反模式学习笔记16 使用随机数排序
目标:随机排序,使用高效的SQL语句查询获取随机数据样本. 反模式:使用RAND()随机函数 SELECT * FROM Employees AS e ORDER BY RAND() Limit 1 ...
- golang学习笔记16 beego orm 数据库操作
golang学习笔记16 beego orm 数据库操作 beego ORM 是一个强大的 Go 语言 ORM 框架.她的灵感主要来自 Django ORM 和 SQLAlchemy. 目前该框架仍处 ...
- C#设计模式学习笔记-单例模式随笔
最近学习 设计模式,从单例模式入手 啥是单例模式: 要实现一个单例类的话,首先,肯定是不能让用户自行生产的,那就是说明不能让用户new,所以,就必须把构造函数设置成为私有的 因为静态变量的生命周期跟整 ...
随机推荐
- 搞定SpringBoot多数据源(3):参数化变更源
目录 1. 引言 2. 参数化变更源说明 2.1 解决思路 2.2 流程说明 3. 实现参数化变更源 3.1 改造动态数据源 3.1.1 动态数据源添加功能 3.1.2 动态数据源配置 3.2 添加数 ...
- GStreamer基础教程13 - 调试Pipeline
摘要 在很多情况下,我们需要对GStreamer创建的Pipeline进行调试,来了解其运行机制以解决所遇到的问题.为此,GStreamer提供了相应的调试机制,方便我们快速定位问题. 查看调试日志 ...
- T117897 七步洗手法 / PJT1(洛谷)
题目:现在有n个人需要依次使用1个洗手池洗手,进行一步洗手需要1单位时间.他们每个人至少会进行一步洗手,但是却不一定进行了完整的七部洗手. 现在你知道了他们总共的洗手时间为t,请你推测他们有多少人进行 ...
- 文件上传二:FormData上传
介绍三种上传方式: 文件上传一:伪刷新上传 文件上传二:FormData上传 文件上传三:base64编码上传 Flash的方式也玩过,现在不推荐用了. 真正的异步上传,FormData的更多操作,请 ...
- Docker获取镜像报错docker: Error response from daemon
docker: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled ...
- 关于selenium自动化元素定位问题解决的几种方法
遇到了元素定位问题和定位到后无法执行点击操作等,闲话少说,直奔主题: 1.元素定位不到一般有如下3种情况,大家如果遇到了可以对号入座哈 a.查找的元素不在当前窗口中 解决方法:使用driver.swi ...
- SpringBoot 的不同
这些在写前端页面的时候,ssm框架中,在页面做出修改之后,保存一下,重新刷新一下浏览器页面就发生了更新 但是sprigBoot中好像不一样,好像是需要对页面进行重新编译一下,浏览器页面才会发生变化 ( ...
- sublime sftp插件安装及时更新网站
Sublime Text 2 本身并不强大,但是它方便使用插件扩展功能,所以变得很强大.今天介绍一个很实用的插件 SFTP ,可以大大提高前端工作效率. 常见的工作流程 有时候修改一些网站上的文件,通 ...
- ActiveMQ 快速入门教程系列 第二章 发布-订阅者模式实现
第二章我们会介绍怎样实现一个发布者对多个订阅者的消息传递 Topic和queue的最大区别在于topic是以广播的形式,通知所有在线监听的客户端有新的消息,没有监听的客户端将收不到消息:而queue则 ...
- JDK的下载及安装教程图解(超详细哦~)
一.本人电脑系统介绍及JDK下载途径 1.先说明一下我的电脑为win10系统,64位操作系统~ 2.我选择下载的JDK版本为1.8版本.给大家来两个下载渠道,方便大家的下载~ JDK官网:https: ...