动机(Motivate):
    在软件构建 过程中,我们需要为某些对象建立一种“通知依赖关系” --------一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面 向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
意图(Intent):
   
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
                                                                         -------《设计模式》GOF
结构图(Struct):
            
适用性:

1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
生活中的例子: 

观 察者定义了对象间一对多的关系,当一个对象的状态变化时,所有依赖它的对象都得到通知并且自动地更新。在ATM取款,当取款成功后,以手机、邮件等方式进行通知。

  
代码实现:



Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->

  1. class BankAccount
  2. {
  3. private Emailer emailer; //强信赖关系
  4.  
  5. private Mobile phoneNumber; //强信赖关系
  6.  
  7. private double _money;
  8.  
  9. public Emailer Emailer
  10. {
  11. get { return emailer; }
  12. set { emailer = value; }
  13. }
  14.  
  15. public Mobile PhoneNumber
  16. {
  17. get { return phoneNumber; }
  18. set { phoneNumber = value; }
  19. }
  20.  
  21. public double Money
  22. {
  23. get { return _money; }
  24. set { _money = value; }
  25. }
  26.  
  27. public void WithDraw()
  28. {
  29. emailer.SendEmail(this);
  30. phoneNumber.SenderNotification(this);
  31.  
  32. }
  33. }
  1. class Emailer
  2. {
  3. private string _emailer;
  4.  
  5. public Emailer(string emailer)
  6. {
  7. this._emailer = emailer;
  8. }
  9.  
  10. public void SendEmail(BankAccount ba)
  11. {
  12. //..
  13. Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emailer, ba.Money);
  14. }
  15. }
  1. class Mobile
  2. {
  3. private long _phoneNumber;
  4.  
  5. public Mobile(long phoneNumber)
  6. {
  7. this._phoneNumber = phoneNumber;
  8. }
  9.  
  10. public void SenderNotification(BankAccount ba)
  11. {
  12. //..
  13. Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, ba.Money);
  14. }
  15. }

客户端调用如下:



Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->

  1. static void Main(string[] args)
  2. {
  3.  
  4. ////------------------------------ 模式1 ----------------------------------------
  5.  
  6. //model1.BankAccount ba = new 模式1.BankAccount();
  7.  
  8. //model1.Emailer emailer = new model1.Emailer("abcdwxc@163.com");
  9. //model1.Mobile mobile = new model1.Mobile(13901234567);
  10.  
  11. //ba.Emailer = emailer;
  12. //ba.PhoneNumber = mobile;
  13.  
  14. //ba.Money = 2000;
  15.  
  16. //ba.WithDraw();
  17.  
  18. ////-------------------------------------------------------------------------------
  19. }

运行结果如下:

    由此可见程序可以正常运行,但请注意BandAccount和Emailer及Mobile之间形成了一种双向的依赖关系,即BankAccount调用了Emailer及Mobile的方法,而Emailer及Mobile调用了BnadAccount类的属性。如果有其中一个类变化,有可能会引起另一个的变化。如果又需添加一种新的通知方式,就得在BankAccount的WithDraw()方法中增加对该中通知方式的调用。
    显然这样的设计极大的违背了“开放-封闭”原则,这不是我们所想要的,仅仅是新增加了一种通知对象,就需要对原有的BankAccount类进行修改,这样的设计是很糟糕的。对此做进一步的抽象,既然出现了多个通知对象,我们就为这些对象之间抽象出一个接口,用它来取消BankAccount和具体的通知对象之间依赖。
由此我们由左图转换到右图。
            
实例代码如下:

  1. interface IObserverAccount
  2. {
  3. void Update(BankAccount ba);
  4. }
 
  1. class BankAccount
  2. {
  3. private List<IObserverAccount> obServers = new List<IObserverAccount>();
  4.  
  5. private double _money;
  6.  
  7. public double Money
  8. {
  9. get { return _money; }
  10. set { _money = value; }
  11. }
  12.  
  13. public void WithDraw()
  14. {
  15. foreach (IObserverAccount ob in obServers)
  16. {
  17. ob.Update(this);
  18. }
  19. }
  20.  
  21. public void AddObServer(IObserverAccount ob)
  22. {
  23. this.obServers.Add(ob);
  24. }
  25.  
  26. public void RemoveObServer(IObserverAccount ob)
  27. {
  28. this.obServers.Remove(ob);
  29. }
  30. }


Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->

  1. class Emailer : Pattern_P16_行为型模式_观察者模式.模式2.IObserverAccount
  2. {
  3. private string _emailer;
  4.  
  5. public Emailer(string emailer)
  6. {
  7. this._emailer = emailer;
  8. }
  9.  
  10. public void Update(BankAccount ba)
  11. {
  12. Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emailer, ba.Money);
  13. }
  14. }
  1. class Mobile : IObserverAccount
  2. {
  3. private long _phoneNumber;
  4.  
  5. public Mobile(long phoneNumber)
  6. {
  7. this._phoneNumber = phoneNumber;
  8. }
  9.  
  10. public void Update(BankAccount ba)
  11. {
  12. Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, ba.Money);
  13. }
  14. }

客户端与上方相同,其运行结果也相同。但BankAccount增加和删除通知对象时,还需对其进行修改。对此我们再做如下重构,在BankAccount中维护一个IObserver列表,同时提供相应的维护方法。

  1. class BankAccount
  2. {
  3. private List<IObserverAccount> obServers = new List<IObserverAccount>();
  4.  
  5. private double _money;
  6.  
  7. public double Money
  8. {
  9. get { return _money; }
  10. set { _money = value; }
  11. }
  12.  
  13. public void WithDraw()
  14. {
  15. foreach (IObserverAccount ob in obServers)
  16. {
  17. ob.Update(this);
  18. }
  19. }
  20.  
  21. public void AddObServer(IObserverAccount ob)
  22. {
  23. this.obServers.Add(ob);
  24. }
  25.  
  26. public void RemoveObServer(IObserverAccount ob)
  27. {
  28. this.obServers.Remove(ob);
  29. }
  30. }

此时客户端代码如下:

  1. ////------------------------------ 模式2 ----------------------------------------
  2.  
  3. //model2.BankAccount ba = new model2.BankAccount();
  4.  
  5. //ba.Money = 2000;
  6.  
  7. //model2.IObserverAccount emailer = new model2.Emailer("abcdwxc@163.com");
  8. //model2.IObserverAccount phone = new model2.Mobile(13901234567);
  9.  
  10. //ba.AddObServer(emailer);
  11. //ba.AddObServer(phone);
  12.  
  13. //ba.WithDraw();
  14.  
  15. ////-------------------------------------------------------------------------------

走到这一步,已经有了Observer模式的影子了,BankAccount类不再依赖于具体的Emailer或Mobile,而是依赖于抽象的IObserverAccount。存在着的一个问题是Emailer或Mobile仍然依赖于具体的BankAccount,解决这样的问题很简单,只需要再对BankAccount类做一次抽象。如下图:
         



Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->

 
  1. internal abstract class Subject
  2. {
  3. private List<IObserverAccount> lObservers = new List<IObserverAccount>();
  4.  
  5. private double _money;
  6.  
  7. public double Money
  8. {
  9. get { return _money; }
  10. set { _money = value; }
  11. }
  12.  
  13. public Subject(double money)
  14. {
  15. this.Money = money;
  16. }
  17.  
  18. public void WithDraw()
  19. {
  20. foreach (IObserverAccount ob in lObservers)
  21. {
  22. ob.Update(this);
  23. }
  24. }
  25.  
  26. public void AddObserverAccount(IObserverAccount ob)
  27. {
  28. lObservers.Add(ob);
  29. }
  30.  
  31. public void RemoveObserverAccount(IObserverAccount ob)
  32. {
  33. lObservers.Remove(ob);
  34. }
  35. }
 
  1. internal interface IObserverAccount
  2. {
  3. void Update(Subject subject);
  4. }
  1. internal class BankAccount:Subject
  2. {
  3. public BankAccount(double money)
  4. : base(money)
  5. {
  6.  
  7. }
  8.  
  9. }
  1. internal class Emailer : Pattern_P16_行为型模式_观察者模式.模式3.IObserverAccount
  2. {
  3. private string _emailer;
  4.  
  5. public Emailer(string emailer)
  6. {
  7. this._emailer = emailer;
  8. }
  9.  
  10. public void Update(Subject subject)
  11. {
  12. Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _emailer, subject.Money);
  13. }
  14. }
  1. internal class Mobile : IObserverAccount
  2. {
  3. private long _phoneNumber;
  4.  
  5. public Mobile(long phoneNumber)
  6. {
  7. this._phoneNumber = phoneNumber;
  8. }
  9.  
  10. public void Update(Subject subject)
  11. {
  12. Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, subject.Money);
  13. }
  14. }

此时客户端实现如下:

  1. ////------------------------------ 模式3 ----------------------------------------
  2.  
  3. //model3.Subject subject = new model3.BankAccount(2000);
  4.  
  5. //model3.IObserverAccount emailer = new model3.Emailer("abcdwxc@163.com");
  6. //model3.IObserverAccount mobile = new model3.Mobile(13901234567);
  7.  
  8. //subject.AddObserverAccount(emailer);
  9. //subject.AddObserverAccount(mobile);
  10.  
  11. //subject.WithDraw();
  12. ////-------------------------------------------------------------------------------

推模式与拉模式
    对于发布-订阅模型,大家都很容易能想到推模式与拉模式,用SQL Server做过数据库复制的朋友对这一点很清楚。在Observer模式中同样区分推模式和拉模式,我先简单的解释一下两者的区别:推模式是当有消息时,把消息信息以参数的形式传递(推)给所有观察者,而拉模式是当有消息时,通知消息的方法本身并不带任何的参数,是由观察者自己到主体对象那儿取回(拉)消息。知道了这一点,大家可能很容易发现上面我所举的例子其实是一种推模式的Observer模式。我们先看看这种模式带来了什么好处:当有消息时,所有的 观察者都会直接得到全部的消息,并进行相应的处理程序,与主体对象没什么关系,两者之间的关系是一种松散耦合。但是它也有缺陷,第一是所有的观察者得到的 消息是一样的,也许有些信息对某个观察者来说根本就用不上,也就是观察者不能“按需所取”;第二,当通知消息的参数有变化时,所有的观察者对象都要变化。鉴于以上问题,拉模式就应运而生了,它是由观察者自己主动去取消息,需要什么信息,就可以取什么,不会像推模式那样得到所有的消息参数。
拉模式实现如下:

  1. internal abstract class Subject
  2. {
  3. private List<IObserverAccount> lObservers = new List<IObserverAccount>();
  4.  
  5. private double _money;
  6.  
  7. public double Money
  8. {
  9. get { return _money; }
  10. set { _money = value; }
  11. }
  12.  
  13. public Subject(double money)
  14. {
  15. this._money = money;
  16. }
  17.  
  18. public void WithDraw()
  19. {
  20. foreach (IObserverAccount ob in lObservers)
  21. {
  22. ob.Update();
  23. }
  24. }
  25.  
  26. public void AddObserver(IObserverAccount ob)
  27. {
  28. lObservers.Add(ob);
  29. }
  30.  
  31. public void RemoveObserver(IObserverAccount ob)
  32. {
  33. lObservers.Remove(ob);
  34. }
  35.  
  36. }
  1. internal interface IObserverAccount
  2. {
  3. void Update();
  4. }
  1. class BankAccount:Subject
  2. {
  3. public BankAccount(double money)
  4. : base(money)
  5. {
  6. }
  7. }


Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->

  1. internal class Emailer : Pattern_P16_行为型模式_观察者模式.模式4.IObserverAccount
  2. {
  3. private string _emailer;
  4. private Subject _subject;
  5.  
  6. public Emailer(string emailer, Subject subject)
  7. {
  8. this._emailer = emailer;
  9. this._subject = subject;
  10. }
  11.  
  12. public void Update()
  13. {
  14. //..
  15. Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emailer, _subject.Money);
  16. }
  17.  
  18. }
  1. class Mobile : IObserverAccount
  2. {
  3. private long _phoneNumber;
  4. public Subject _subject;
  5.  
  6. public Mobile(long phoneNumber, Subject subject)
  7. {
  8. this._phoneNumber = phoneNumber;
  9. this._subject = subject;
  10. }
  11.  
  12. public void Update()
  13. {
  14. Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _phoneNumber, _subject.Money);
  15. }
  16. }

此时客户端调用如下:



Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->

  1. ////------------------------------ 模式4 ----------------------------------------
  2.  
  3. //model4.Subject subject = new model4.BankAccount(2000);
  4.  
  5. //model4.IObserverAccount emailer = new model4.Emailer("abcdwxc@163.com", subject);
  6. //model4.IObserverAccount mobile = new model4.Mobile(13901234567, subject);
  7.  
  8. //subject.AddObserver(emailer);
  9. //subject.AddObserver(mobile);
  10.  
  11. //subject.WithDraw();
  12.  
  13. ////-------------------------------------------------------------------------------

.NET中Observer实现:
   
用事件和委托来实现Observer模式我认为更加的简单和优雅,也是一种更好的解决方案。



Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->

  1. internal class Subject
  2. {
  3. public event DelegateClass.NotifyEventHander notifyEvent;
  4.  
  5. private double _money;
  6.  
  7. public double Money
  8. {
  9. get { return _money; }
  10. set { _money = value; }
  11. }
  12.  
  13. public Subject(double money)
  14. {
  15. this._money = money;
  16. }
  17.  
  18. public void WithDraw()
  19. {
  20. OnNotifyChange();
  21. }
  22.  
  23. public void OnNotifyChange()
  24. {
  25. if (notifyEvent != null)
  26. {
  27. notifyEvent(this);
  28. }
  29.  
  30. }
  31. }
  1. class Emailer
  2. {
  3. private string _emailer;
  4.  
  5. public Emailer(string emailer)
  6. {
  7. this._emailer = emailer;
  8. }
  9.  
  10. public void Update(object obj)
  11. {
  12. if (obj is Subject)
  13. {
  14. Subject subject = (Subject)obj;
  15.  
  16. Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emailer, subject.Money);
  17. }
  18. }
  19.  
  20. }
  1. public delegate void NotifyEventHandler(object sender);
  2. 客户端调用如

客户端调用如下:

  1. //------------------------------ 模式5 ----------------------------------------
  2.  
  3. model5.Subject subject = new model5.Subject();
  4.  
  5. model5.Emailer emailer = new model5.Emailer("abcdwxc@163.com");
  6. model5.Mobile mobile = new model5.Mobile();
  7.  
  8. subject.notifyEvent += new model5.DelegateClass.NotifyEventHander(emailer.Update);
  9. subject.notifyEvent += new model5.DelegateClass.NotifyEventHander(mobile.Update);
  10.  
  11. subject.WithDraw();
  12.  
  13. //-------------------------------------------------------------------------------

Observer实现要点:

1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。

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

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

16.观察者模式(Observer Pattern)的更多相关文章

  1. 设计模式 - 观察者模式(Observer Pattern) 详细说明

    观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...

  2. 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)

    原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...

  3. 设计模式 - 观察者模式(Observer Pattern) 详细解释

    观察者模式(Observer Pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...

  4. 设计模式-观察者模式(Observer Pattern)

    观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 观察者 ...

  5. jQuery中的观察者模式(Observer Pattern)

    在jQuery中,on方法可以为元素绑定事件,trigger方法可以手动触发事件,围绕这2个方法,我们来体验jQuery中的观察者模式(Observer Pattern). ■ on方法绑定内置事件, ...

  6. 设计模式 - 观察者模式(Observer Pattern) Java内置 用法

    观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...

  7. 二十四种设计模式:观察者模式(Observer Pattern)

    观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...

  8. 使用C# (.NET Core) 实现观察者模式 (Observer Pattern) 并介绍 delegate 和 event

    观察者模式 这里面综合了几本书的资料. 需求 有这么个项目: 需求是这样的: 一个气象站, 有三个传感器(温度, 湿度, 气压), 有一个WeatherData对象, 它能从气象站获得这三个数据. 还 ...

  9. php观察者模式(observer pattern)

    ... <?php /* The observer pattern implements a one-too-many dependency between objects. The objec ...

  10. C#设计模式——观察者模式(Observer Pattern)1

    一.概述在软件设计工作中会存在对象之间的依赖关系,当某一对象发生变化时,所有依赖它的对象都需要得到通知.如果设计的不好,很容易造成对象之间的耦合度太高,难以应对变化.使用观察者模式可以降低对象之间的依 ...

随机推荐

  1. BZOJ3513[MUTC2013]idiots——FFT+生成函数

    题目描述 给定n个长度分别为a_i的木棒,问随机选择3个木棒能够拼成三角形的概率. 输入 第一行T(T<=100),表示数据组数. 接下来若干行描述T组数据,每组数据第一行是n,接下来一行有n个 ...

  2. MySQL数据库实现分页查询的SQL语句写法!

    一:分页需求: 客户端通过传递start(页码),limit(每页显示的条数)两个参数去分页查询数据库表中的数据,那我们知道MySql数据库提供了分页的函数limit m,n,但是该函数的用法和我们的 ...

  3. python学习日记(文件操作)

    文件操作概述 计算机系统分为:操作系统,计算机硬件,应用程序. 我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,众所周知,应用程序 ...

  4. LGP2801 教主的魔法

    题目链接 : P2801 教主的魔法 这是第一次A分块的题 就是模板题了 每个块内排序 每个整块仅需维护整块的修改量 询问操作: 对于边缘块 直接暴力找在[l, r]内 且比给定值大的有几个 对于整块 ...

  5. LVS-Keepalived高可用集群(NAT)

    LEA-6-LVS-NAT+Keepalived高可用集群-------client-----------------主LVS-----------------从LVS---------------- ...

  6. 可以精准解析国内外网站的DNS

    Pure DNS https://puredns.cn/ XsicoDNS https://dns.xsico.cn/ Fun DNS http://www.fundns.cn/ Bai DNS ht ...

  7. tyvj/joyoi 2018 小猫爬山

    2018,这个题号吼哇! 搜索第一题,巨水. WA了一次,因为忘了还原... #include <cstdio> ; int n, W, ans, weigh[N], cost[N]; i ...

  8. centos7安装saltstack

    环境是Cenos7 saltstack-master:192.168.0.140 saltstack-minion:192.168.0.141 安装epel yum源 yum -y install e ...

  9. Django反正解析路由

    首先要了解为什么要定义反响解析 因为随着功能的增加会出先非常多的视图和对应的路由,有可能项目的一些需要一些模板需要重写路由,或之前配置的正则表达式不够准确,于是就要修改正则表达式,但是路由或正则表达式 ...

  10. python基础面试常见题

    1.为什么学习Python? Python是目前市面上,我个人认为是最简洁.最优雅.最有前途.最全能的编程语言,没有之一. 2.通过什么途径学习的Python? 通过自学,包括网上查看一些视频,购买一 ...