热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

D17:C#设计模式之十六观察者模式(ObserverPattern)【行为型】

一、引言今天是2017年11月份的最后一天,也就是2017年11月30日,利用今天再写一个模式,争取下个月(也就是12月份&

一、引言

   今天是2017年11月份的最后一天,也就是2017年11月30日,利用今天再写一个模式,争取下个月(也就是12月份)把所有的模式写完,2018年,新的一年写一些新的东西。今天我们开始讲“行为型”设计模式的第四个模式,该模式是【观察者模式】,英文名称是:Observer Pattern。还是老套路,先从名字上来看看。“观察者模式”我第一次看到这个名称,我的理解是,既然有“观察者”,那肯定就有“被观察者”了,“观察者”监视着“被观察者”,如果“被观察者”有所行动,“观察者”就会做出相应的动作来回应,哈哈,听起来是不是有点像“谍战”的味道。我所说的谍战不是天朝内的那种,比如:手撕鬼子,我说的是“谍影重重”的那类优秀影片,大家懂得。“观察者模式”在现实生活中,实例其实是很多的,比如:八九十年代我们订阅的报纸,我们会定期收到报纸,因为我们订阅了。银行可以给储户发手机短信,也是“观察者模式”很好的使用的例子,因为我们订阅了银行的短信业务,当我们账户余额发生变化就会收到通知,还有很多,我就不一一列举了,发挥大家的想象吧。好了,接下来,就让我们看看该模式具体是怎么实现的吧。

二、观察者模式的详细介绍

2.1、动机(Motivate)

   在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。

   使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

2.2、意图(Intent)

   定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。                                      ——《设计模式》GoF

2.3、结构图

       

2.4、模式的组成
    
    可以看出,在观察者模式的结构图有以下角色:

    (1)、抽象主题角色(Subject):抽象主题把所有观察者对象的引用保存在一个列表中,并提供增加和删除观察者对象的操作,抽象主题角色又叫做抽象被观察者角色,一般由抽象类或接口实现。

    (2)、抽象观察者角色(Observer):为所有具体观察者定义一个接口,在得到主题通知时更新自己,一般由抽象类或接口实现。

    (3)、具体主题角色(ConcreteSubject):实现抽象主题接口,具体主题角色又叫做具体被观察者角色。

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

2.5、观察者模式的代码实现

    观察者模式在显示生活中也有类似的例子,比如:我们订阅银行短信业务,当我们账户发生改变,我们就会收到相应的短信。类似的还有微信订阅号,今天我们就以银行给我发送短信当我们账户余额发生变化的时候为例来讲讲观察者模式的实现,很简单,现实生活正例子也很多,理解起来也很容易。我们看代码吧,实现代码如下:

1 namespace 观察者模式的实现2 {3 //银行短信系统抽象接口&#xff0c;是被观察者--该类型相当于抽象主体角色Subject4 public abstract class BankMessageSystem5 {6 protected IList observers;7 8 //构造函数初始化观察者列表实例9 protected BankMessageSystem()10 {11 observers &#61; new List();12 }13 14 //增加预约储户15 public abstract void Add(Depositor depositor);16 17 //删除预约储户18 public abstract void Delete(Depositor depositor);19 20 //通知储户21 public void Notify()22 {23 foreach (Depositor depositor in observers)24 {25 if (depositor.AccountIsChanged)26 {27 depositor.Update(depositor.Balance, depositor.OperationDateTime);28 //账户发生了变化&#xff0c;并且通知了&#xff0c;储户的账户就认为没有变化29 depositor.AccountIsChanged &#61; false;30 }31 }32 }33 }34 35 //北京银行短信系统&#xff0c;是被观察者--该类型相当于具体主体角色ConcreteSubject36 public sealed class BeiJingBankMessageSystem : BankMessageSystem37 {38 //增加预约储户39 public override void Add(Depositor depositor)40 {41 //应该先判断该用户是否存在&#xff0c;存在不操作&#xff0c;不存在则增加到储户列表中&#xff0c;这里简化了42 observers.Add(depositor);43 }44 45 //删除预约储户46 public override void Delete(Depositor depositor)47 {48 //应该先判断该用户是否存在&#xff0c;存在则删除&#xff0c;不存在无操作&#xff0c;这里简化了49 observers.Remove(depositor);50 }51 }52 53 //储户的抽象接口--相当于抽象观察者角色&#xff08;Observer&#xff09;54 public abstract class Depositor55 {56 //状态数据57 private string _name;58 private int _balance;59 private int _total;60 private bool _isChanged;61 62 //初始化状态数据63 protected Depositor(string name, int total)64 {65 this._name &#61; name;66 this._balance &#61; total;//存款总额等于余额67 this._isChanged &#61; false;//账户未发生变化68 }69 70 //储户的名称&#xff0c;假设可以唯一区别的71 public string Name72 {73 get { return _name; }74 private set { this._name &#61; value; }75 }76 77 public int Balance78 {79 get { return this._balance; }80 }81 82 //取钱83 public void GetMoney(int num)84 {85 if (num <&#61; this._balance && num > 0)86 {87 this._balance &#61; this._balance - num;88 this._isChanged &#61; true;89 OperationDateTime &#61; DateTime.Now;90 }91 }92 93 //账户操作时间94 public DateTime OperationDateTime { get; set; }95 96 //账户是否发生变化97 public bool AccountIsChanged98 {99 get { return this._isChanged; }
100 set { this._isChanged &#61; value; }
101 }
102
103 //更新储户状态
104 public abstract void Update(int currentBalance, DateTime dateTime);
105 }
106
107 //北京的具体储户--相当于具体观察者角色ConcreteObserver
108 public sealed class BeiJingDepositor : Depositor
109 {
110 public BeiJingDepositor(string name, int total) : base(name, total) { }
111
112 public override void Update(int currentBalance, DateTime dateTime)
113 {
114 Console.WriteLine(Name &#43; ":账户发生了变化&#xff0c;变化时间是" &#43; dateTime.ToString() &#43; ",当前余额是" &#43; currentBalance.ToString());
115 }
116 }
117
118
119 // 客户端&#xff08;Client&#xff09;
120 class Program
121 {
122 static void Main(string[] args)
123 {
124 //我们有了三位储户&#xff0c;都是武林高手&#xff0c;也比较有钱
125 Depositor huangFeiHong &#61; new BeiJingDepositor("黄飞鸿", 3000);
126 Depositor fangShiYu &#61; new BeiJingDepositor("方世玉", 1300);
127 Depositor hongXiGuan &#61; new BeiJingDepositor("洪熙官", 2500);
128
129 BankMessageSystem beijingBank &#61; new BeiJingBankMessageSystem();
130 //这三位开始订阅银行短信业务
131 beijingBank.Add(huangFeiHong);
132 beijingBank.Add(fangShiYu);
133 beijingBank.Add(hongXiGuan);
134
135 //黄飞鸿取100块钱
136 huangFeiHong.GetMoney(100);
137 beijingBank.Notify();
138
139 //黄飞鸿和方世玉都取了钱
140 huangFeiHong.GetMoney(200);
141 fangShiYu.GetMoney(200);
142 beijingBank.Notify();
143
144 //他们三个都取了钱
145 huangFeiHong.GetMoney(320);
146 fangShiYu.GetMoney(4330);
147 hongXiGuan.GetMoney(332);
148 beijingBank.Notify();
149
150 Console.Read();
151 }
152 }
153 }


 观察者模式有些麻烦的地方就是关于状态的处理&#xff0c;我这里面涉及了一些状态的处理&#xff0c;大家可以细细体会一下&#xff0c;模式还是要多多练习&#xff0c;多多写&#xff0c;里面的道理就不难理解了。
   
三、观察者模式的实现要点&#xff1a;
    
    使用面向对象的抽象&#xff0c;Observer模式使得我们可以独立地改变目标与观察者&#xff08;面向对象中的改变不是指改代码&#xff0c;而是指扩展、子类化、实现接口&#xff09;&#xff0c;从而使二者之间的依赖关系达致松耦合。

    目标发送通知时&#xff0c;无需指定观察者&#xff0c;通知&#xff08;可以携带通知信息作为参数&#xff09;会自动传播。观察者自己决定是否需要订阅通知&#xff0c;目标对象对此一无所知。

    在C#的event中&#xff0c;委托充当了抽象的Observer接口&#xff0c;而提供事件的对象充当了目标对象。委托是比抽象Observer接口更为松耦合的设计。

     3.1】、观察者模式的优点&#xff1a;

          &#xff08;1&#xff09;、观察者模式实现了表示层和数据逻辑层的分离&#xff0c;并定义了稳定的更新消息传递机制&#xff0c;并抽象了更新接口&#xff0c;使得可以有各种各样不同的表示层&#xff0c;即观察者。

          &#xff08;2&#xff09;、观察者模式在被观察者和观察者之间建立了一个抽象的耦合&#xff0c;被观察者并不知道任何一个具体的观察者&#xff0c;只是保存着抽象观察者的列表&#xff0c;每个具体观察者都符合一个抽象观察者的接口。

          &#xff08;3&#xff09;、观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知。

    3.2】、观察者模式的缺点&#xff1a;

         &#xff08;1&#xff09;、如果一个被观察者有很多直接和间接的观察者时&#xff0c;将所有的观察者都通知到会花费很多时间。

         &#xff08;2&#xff09;、虽然观察者模式可以随时使观察者知道所观察的对象发送了变化&#xff0c;但是观察者模式没有相应的机制使观察者知道所观察的对象是怎样发生变化的。

         &#xff08;3&#xff09;、如果在被观察者之间有循环依赖的话&#xff0c;被观察者会触发它们之间进行循环调用&#xff0c;导致系统崩溃&#xff0c;在使用观察者模式应特别注意这点。


四、.NET 中观察者模式的实现

     我上面写了一点&#xff0c;“在C#的event中&#xff0c;委托充当了抽象的Observer接口&#xff0c;而提供事件的对象充当了目标对象。委托是比抽象Observer接口更为松耦合的设计。”&#xff0c;其实在Net里面实现的观察者模式做了一些改变&#xff0c;用委托或者说是事件来实现观察者模式。事件我们都很明白&#xff0c;我们可以注册控件的事件&#xff0c;当触发控件的动作时候&#xff0c;相应的事件就会执行&#xff0c;在事件的执行过程中我们就可以做相关的提醒业务。这里关于观察者模式在Net里面的实现就不说了&#xff0c;如果大家不明白&#xff0c;可以多看看相关委托或者事件的相关资料。

五、总结

    终于写完了&#xff0c;这个模式主要是花在了代码的书写上。因为我写每篇文章的时候&#xff0c;模式实现代码都是当时现想的&#xff0c;要组织代码关系&#xff0c;让其更合理&#xff0c;所以时间就花了不少&#xff0c;但是是理解更好了。该模式不是很难&#xff0c;结构也不是很复杂&#xff0c;唯一让我们多多注意的是状态的管理。这个模式结合实例理解是很容易的&#xff0c;模式的使用我们不能照搬&#xff0c;要理解&#xff0c;当然多多的联系和写代码也是必不可少的&#xff0c;我们使用模式的一贯宗旨是通过重构和迭代&#xff0c;在我们的代码中实现相应的模式。


推荐阅读
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 本文介绍了GitHub上的一些Python开源项目,特别是IM(即时通讯)技术的应用。通过Sealtalk项目,探讨了如何利用开源SDK提升开发效率。 ... [详细]
  • pypy 真的能让 Python 比 C 还快么?
    作者:肖恩顿来源:游戏不存在最近“pypy为什么能让python比c还快”刷屏了,原文讲的内容偏理论,干货比较少。我们可以再深入一点点,了解pypy的真相。正式开始之前,多唠叨两句 ... [详细]
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • 本文详细介绍如何在 Apache 中设置虚拟主机,包括基本配置和高级设置,帮助用户更好地理解和使用虚拟主机功能。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 本文介绍了一种使用SQL Server存储过程来实现基于单一条件的高效分页查询的方法。通过示例代码,详细说明了如何构建和执行这种分页查询。 ... [详细]
  • 本文深入探讨了Go语言中的接口型函数,通过实例分析其灵活性和强大功能,帮助开发者更好地理解和运用这一特性。 ... [详细]
  • 如何将955万数据表的17秒SQL查询优化至300毫秒
    本文详细介绍了通过优化SQL查询策略,成功将一张包含955万条记录的财务流水表的查询时间从17秒缩短至300毫秒的方法。文章不仅提供了具体的SQL优化技巧,还深入探讨了背后的数据库原理。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 本教程介绍如何在C#中通过递归方法将具有父子关系的列表转换为树形结构。我们将详细探讨如何处理字符串类型的键值,并提供一个实用的示例。 ... [详细]
  • 原文地址:https:blog.csdn.netqq_35361471articledetails84715491原文地址:https:blog.cs ... [详细]
  • c#  项目文件,C#viual studio使用方法
    一、项目文件1)Properties节点下主要存放的是当前程序集相关的信息,如版本号、标题等。双击”Properties“,打开如下项目属 ... [详细]
  • C# 实现高效分页控件
    在使用 C# 进行数据库开发时,分页功能是常见的需求。为了避免每次编写重复的分页代码,我开发了一个用户控件,使分页操作变得更加简便。 ... [详细]
author-avatar
VW旻shi只吃货8453
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有