本笔记摘抄自: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)观察者模式的更多相关文章

  1. Java设计模式学习笔记(观察者模式)

    观察者模式说起来很简单,就是一个订报纸的模式.但是实际上这部分我觉得还是很有意思的,<Head First设计模式>里还有一些还没看完,也是因为理解的不够深吧. 观察者模式会包含两个组件: ...

  2. 设计模式学习笔记——Observer观察者模式

    观察者模式里面有两个东西:观察者(Observer)和目标(Subject).当目标发生变化的时候,观察者随之起舞,也作出相应的变化.此为观察者模式. 这是怎么做到的?主要是目标里面存有一份观察者的名 ...

  3. Java设计模式学习笔记(二) 简单工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...

  4. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  5. Java设计模式学习笔记(五) 单例模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 使用单例模式的原因 以Windows任务管理器为例,在Windows系统中,任务管理器是唯 ...

  6. 7 种 Javascript 常用设计模式学习笔记

    7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...

  7. Ext.Net学习笔记16:Ext.Net GridPanel 折叠/展开行

    Ext.Net学习笔记16:Ext.Net GridPanel 折叠/展开行 Ext.Net GridPanel的行支持折叠/展开功能,这个功能个人觉得还说很有用处的,尤其是数据中包含图片等内容的时候 ...

  8. SQL反模式学习笔记16 使用随机数排序

    目标:随机排序,使用高效的SQL语句查询获取随机数据样本. 反模式:使用RAND()随机函数 SELECT * FROM Employees AS e ORDER BY RAND() Limit 1 ...

  9. golang学习笔记16 beego orm 数据库操作

    golang学习笔记16 beego orm 数据库操作 beego ORM 是一个强大的 Go 语言 ORM 框架.她的灵感主要来自 Django ORM 和 SQLAlchemy. 目前该框架仍处 ...

  10. C#设计模式学习笔记-单例模式随笔

    最近学习 设计模式,从单例模式入手 啥是单例模式: 要实现一个单例类的话,首先,肯定是不能让用户自行生产的,那就是说明不能让用户new,所以,就必须把构造函数设置成为私有的 因为静态变量的生命周期跟整 ...

随机推荐

  1. C++ 引用分析

    引用 左值引用,建立既存对象的别名 右值引用,可用于为临时对象延长生命周期 转发引用,保持函数实参的类别 悬置引用,对象生命周期已经结束的引用,访问改引用为未定义行为 值类别,左值,纯右值,亡值 st ...

  2. python接口自动化测试 - configparser配置文件解析器详细使用

    configparser简介 ConfigParser模块已在Python 3中重命名为configparser 该模块定义了ConfigParser类. ConfigParser类实现一种基本的配置 ...

  3. scanf 函数笔记

    函数声明 int scanf(const char *format, ...); 说明 <返回值> scanf ("<格式化字符串>", <参数表&g ...

  4. Centos7使用docker搭建Sentry

    1.安装docker Sentry 是一款基于 Django实现的错误日志收集和聚合的平台,它是 Python 实现的,但是其日志监控功能却不局限于python,对诸如 Node.js, php,ru ...

  5. excle 写入数据库

    龙龙博客:https://www.cnblogs.com/meilong/p/cao-zuoexcel-mo-kuaiopenpyxl.html 1 安装 pip install openpyxl 如 ...

  6. css的字体单位

    在css中的字体单位主要以px.em.rem为主.其中px也就是像素,是一种字体长度,它的长度是相对于显示器的品目分辨率而言的.一般情况下在浏览器中默认字体的大小是16px.其中em是相对字体.em的 ...

  7. ios---apple mach-o linker error 报错解决

    问题触发场景 在新xcode环境里配置了cocoapods,并运行了自己的项目.然后某日从其他地方clone了第三方项目,打开后,有了这个报错: 问题原因 1.用cocoapods装了第三方插件后,要 ...

  8. 记一次阿里云LVM扩容与 LVM 相关知识学习

    一.lvm 扩容 问题: 我们阿里云服务器有一个磁盘容量为 1T ,但是最近由于业务的扩增,磁盘容量已经不够了,需要增大磁盘的容量.磁盘挂载在 /home,使用的是 LVM.我们现在需要对磁盘进行扩容 ...

  9. 小白学Java:RandomAccessFile

    目录 小白学Java:RandomAccessFile 概述 继承与实现 构造器 模式设置 文件指针 操作数据 读取数据 read(byte b[])与read() 追加数据 插入数据 小白学Java ...

  10. node使用art-template的过滤器

    引言 art-template过滤器在我看来,其实就是定义一个函数,模板字符串通过调用该函数处理相关的数据,得到相应的返回结果,显示在页面上.因此我们可以注册一个过滤器,处理相关的数据.这里使用nod ...