本笔记摘抄自: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 > 0) { 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("黄飞鸿", 3000); Depositor fangShiYu = new GuangDongDepositor("方世玉", 1300); Depositor hongXiGuan = new GuangDongDepositor("洪熙官", 2500); BankMessageSystem guangDongBank = new GuangDongBankMessageSystem(); //这三位开始订阅银行短信业务 guangDongBank.Add(huangFeiHong); guangDongBank.Add(fangShiYu); guangDongBank.Add(hongXiGuan); //早上黄飞鸿取了100块钱 huangFeiHong.GetMoney(100); guangDongBank.Notify(); //中午黄飞鸿和方世玉各取了200块 huangFeiHong.GetMoney(200); fangShiYu.GetMoney(200); guangDongBank.Notify(); //晚上他们三个都取了钱 huangFeiHong.GetMoney(300); fangShiYu.GetMoney(300); hongXiGuan.GetMoney(300); guangDongBank.Notify(); Console.Read(); #endregion } }