C#设计模式之十六观察者模式(Observer Pattern)【行为型】

简介: 原文:C#设计模式之十六观察者模式(Observer Pattern)【行为型】一、引言   今天是2017年11月份的最后一天,也就是2017年11月30日,利用今天再写一个模式,争取下个月(也就是12月份)把所有的模式写完,2018年,新的一年写一些新的东西。
原文: C#设计模式之十六观察者模式(Observer Pattern)【行为型】

一、引言

   今天是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     //银行短信系统抽象接口,是被观察者--该类型相当于抽象主体角色Subject
  4     public abstract class BankMessageSystem
  5     {
  6         protected IList<Depositor> observers;
  7 
  8         //构造函数初始化观察者列表实例
  9         protected BankMessageSystem()
 10         {
 11             observers = new List<Depositor>();
 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                     //账户发生了变化,并且通知了,储户的账户就认为没有变化
 29                     depositor.AccountIsChanged = false;
 30                 }
 31             }
 32         }
 33     }
 34 
 35     //北京银行短信系统,是被观察者--该类型相当于具体主体角色ConcreteSubject
 36     public sealed class BeiJingBankMessageSystem : BankMessageSystem
 37     {
 38         //增加预约储户
 39         public override void Add(Depositor depositor)
 40         {
 41             //应该先判断该用户是否存在,存在不操作,不存在则增加到储户列表中,这里简化了
 42             observers.Add(depositor);
 43         }
 44 
 45         //删除预约储户
 46         public override void Delete(Depositor depositor)
 47         {
 48             //应该先判断该用户是否存在,存在则删除,不存在无操作,这里简化了
 49             observers.Remove(depositor);
 50         }
 51     }
 52 
 53     //储户的抽象接口--相当于抽象观察者角色(Observer)
 54     public abstract class Depositor
 55     {
 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 = name;
 66             this._balance = total;//存款总额等于余额
 67             this._isChanged = false;//账户未发生变化
 68         }
 69 
 70         //储户的名称,假设可以唯一区别的
 71         public string Name
 72         {
 73             get { return _name; }
 74             private set { this._name = value; }
 75         }
 76 
 77         public int Balance
 78         {
 79             get { return this._balance; }
 80         }
 81 
 82         //取钱
 83         public void GetMoney(int num)
 84         {
 85             if (num <= this._balance && num > 0)
 86             {
 87                 this._balance = this._balance - num;
 88                 this._isChanged = true;
 89                 OperationDateTime = DateTime.Now;
 90             }
 91         }
 92 
 93         //账户操作时间
 94         public DateTime OperationDateTime { get; set; }
 95 
 96         //账户是否发生变化
 97         public bool AccountIsChanged
 98         {
 99             get { return this._isChanged; }
100             set { this._isChanged = 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 + ":账户发生了变化,变化时间是" + dateTime.ToString() + ",当前余额是" + currentBalance.ToString());
115         }
116     }
117 
118 
119     // 客户端(Client)
120     class Program
121     {
122         static void Main(string[] args)
123         {
124             //我们有了三位储户,都是武林高手,也比较有钱
125             Depositor huangFeiHong = new BeiJingDepositor("黄飞鸿", 3000);
126             Depositor fangShiYu = new BeiJingDepositor("方世玉", 1300);
127             Depositor hongXiGuan = new BeiJingDepositor("洪熙官", 2500);
128 
129             BankMessageSystem beijingBank = 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 }


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

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

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

     3.1】、观察者模式的优点:

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

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

          (3)、观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知。

    3.2】、观察者模式的缺点:

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

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

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


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

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

五、总结

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

目录
相关文章
|
8天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
4天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
14 3
|
15天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
32 9
|
14天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
27 2
|
23天前
|
设计模式 监控 UED
设计模式之观察者模式
【10月更文挑战第12天】 观察者模式是一种行为型设计模式,定义了一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。主要由主题(被观察者)和观察者组成,实现对象间的松耦合,广泛应用于用户界面、事件驱动系统和数据监控等领域。
|
25天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
本教程详细讲解Kotlin语法,适合深入学习。对于快速掌握Kotlin,推荐“简洁”系列教程。本文特别介绍了观察者模式,包括使用Java API和Kotlin委托属性(如Delegates.observable)实现的方法,旨在帮助开发者更高效地实现和优化观察者模式的应用。
31 3
|
24天前
|
设计模式 安全 Java
C# 一分钟浅谈:设计模式之单例模式
【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy&lt;T&gt;` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。
29 1
|
6月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
188 3
|
7天前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
19 3
|
28天前
|
安全 C# 数据安全/隐私保护
实现C#编程文件夹加锁保护
【10月更文挑战第16天】本文介绍了两种用 C# 实现文件夹保护的方法:一是通过设置文件系统权限,阻止普通用户访问;二是使用加密技术,对文件夹中的文件进行加密,防止未授权访问。提供了示例代码和使用方法,适用于不同安全需求的场景。