博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式:观察者模式(Observer Pattern)
阅读量:6311 次
发布时间:2019-06-22

本文共 8609 字,大约阅读时间需要 28 分钟。

作者:  创建于:2006-07-17 出处: 收录于:2006-10-23  收录于:2013-03-01

结构图


 

意图


 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时所有依赖于它的对象都得到通知并被自动更新。

适用性


  • 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  • 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。 

实现代码


下面通过一个例子来说明Observer模式。监控某一个公司的股票价格变化,可以有多种方式,通知的对象可以是投资者,或者是发送到移动设备,还有电子邮件等。一开始我们先不考虑Observer模式,通过一步步地重构,最终重构为Observer模式。现在有这样两个类:MicrosoftInvestor,如下图所示:

View Code
1 using System; 2     public class Microsoft 3     { 4         private Investor _investor; 5         private String _symbol; 6         private double _price; 7         public void Update() 8         { 9             _investor.SendData(this);10         }11         public Investor Investor12         {13             get { return _investor; }14             set { _investor = value; }15         }16         public String Symbol17         {18             get { return _symbol; }19             set { _symbol = value; }20         }21         public double Price22         {23             get { return _price; }24             set { _price = value; }25         }26     }27     public class Investor28     {29         private string _name;30         public Investor(string name)31         {32             this._name = name;33         }34         public void SendData(Microsoft ms)35         {36             Console.WriteLine("Notified {0}: Company '{1}' " + "change to {2:C}", _name, ms.Symbol, ms.Price);37         }38     }39     //客户端代码40     class Program41     {42         static void Main(string[] args)43         {44             Investor investor = new Investor("Jom");45             Microsoft ms = new Microsoft();46             ms.Investor = investor;47             ms.Symbol = "Microsoft";48             ms.Price = 120.00;49             ms.Update();50             Console.ReadLine();51         }52     }

可以看到,这段代码运行并没有问题,也确实实现了我们最初的设想的功能,把Microsoft的股票价格变化通知到了Jom投资者那儿。但是这里面出现了如下几个问题:

(1)Microsoft和Investor之间形成了一种双向的依赖关系,即Microsoft调用了Investor的方法,而Investor调用了Microsoft类的属性。如果有其中一个类变化,有可能会引起另一个的变化。

(2)当出现一种的通知对象,比如说是移动设备Mobile:

既然出现了多个通知对象,我们就为这些对象之间抽象出一个接口,用它来取消Microsoft和具体的通知对象之间依赖。

 

 
View Code
1     using System; 2     using System.Collections.Generic; 3  4     public interface IObserver 5     { 6         void SendData(Microsoft ms); 7     } 8     public class Investor : IObserver 9     {10         private string _name;11         public Investor(string name)12         {13             this._name = name;14         }15 16         public void SendData(Microsoft ms)17         {18             Console.WriteLine("Notified {0}: Company '{1}' " + "change to {2:C}", _name, ms.Symbol, ms.Price);19         }20     }21 22     public class Microsoft23     {24         private List
observers = new List
();25 private String _symbol;26 private double _price;27 public String Symbol28 {29 get { return _symbol; }30 set { _symbol = value; }31 }32 public double Price33 {34 get { return _price; }35 set { _price = value; }36 }37 38 public void Update()39 {40 foreach (IObserver ob in observers)41 {42 ob.SendData(this);43 }44 }45 46 public void AddObserver(IObserver observer)47 {48 observers.Add(observer);49 }50 public void RemoveObserver(IObserver observer)51 {52 observers.Remove(observer);53 } 54 }55 56 //客户端代码57 class Program58 {59 static void Main(string[] args)60 {61 IObserver investor1 = new Investor("Jom");62 IObserver investor2 = new Investor("TerryLee");63 Microsoft ms = new Microsoft();64 ms.Symbol = "Microsoft";65 ms.Price = 120.00;66 ms.AddObserver(investor1);67 ms.AddObserver(investor2);68 ms.Update();69 Console.ReadLine();70 }71 }

走到这一步,已经有了Observer模式的影子了,Microsoft类不再依赖于具体的Investor,而是依赖于抽象的IOberver。存在着的一个问题是Investor仍然依赖于具体的公司Microsoft,况且公司还会有很多IBMGoogle等,解决这样的问题很简单,只需要再对Microsoft类做一次抽象。如下图所示:

View Code
1 namespace Program 2 { 3     using System; 4     using System.Collections.Generic; 5     public interface IObserver 6     { 7         void SendData(Stock ms); 8     } 9     public class Investor : IObserver10     {11         private string _name;12         public Investor(string name)13         {14             this._name = name;15         }16         public void SendData(Stock ms)17         {18             Console.WriteLine("Notified {0}: Company '{1}' " + "change to {2:C}", _name, ms.Symbol, ms.Price);19         }20     }21     public abstract class Stock22     {23         private List
observers = new List
();24 private String _symbol;25 private double _price;26 public Stock(String symbol, double price)27 {28 this._symbol = symbol;29 this._price = price;30 }31 public void Update()32 {33 foreach (IObserver ob in observers)34 {35 ob.SendData(this);36 }37 }38 public void AddObserver(IObserver observer)39 {40 observers.Add(observer);41 }42 public void RemoveObserver(IObserver observer)43 {44 observers.Remove(observer);45 }46 public String Symbol47 {48 get { return _symbol; }49 }50 public double Price51 {52 get { return _price; }53 }54 }55 public class Microsoft : Stock56 {57 public Microsoft(String symbol, double price)58 : base(symbol, price)59 { }60 }61 //客户端代码62 class Program63 {64 static void Main(string[] args)65 {66 Stock ms = new Microsoft("Microsoft", 120.00);67 ms.AddObserver(new Investor("Jom"));68 ms.AddObserver(new Investor("TerryLee"));69 ms.Update();70 Console.ReadLine();71 }72 }73 }

到这里我们可以看到,通过不断的重构,不断地抽象,我们由一开始的很糟糕的设计,逐渐重构为使用Observer模式的这样一个方案。在这个例子里面,IOberser充当了观察者的角色,而Stock则扮演了主题对象角色,在任何时候,只要调用了StockUpdate()方法,它就会通知它的所有观察者对象。同时可以看到,通过Observer模式,取消了直接依赖,变为间接依赖,这样大大提供了系统的可维护性和可扩展性。

.NET中的Observer模式


在.NET中,相信大家对于事件和委托都已经不陌生了,这里就不具体多说了。利用事件和委托来实现Observer模式我认为更加的简单和优雅,也是一种更好的解决方案。因为在上面的示例中我们可以看到,虽然取消了直接耦合,但是又引入了不必要的约束(暂且这么说吧)。即那些子类必须都继承于主题父类,还有观察者接口等。网上有很多这方面的例子,上面的例子简单的用事件和委托实现如下,仅供大家参考:

View Code
1 using System; 2     using System.Collections.Generic; 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             Stock stock = new Stock("Microsoft", 120.00); 8             Investor investor = new Investor("Jom"); 9             stock.NotifyEvent += new NotifyEventHandler(investor.SendData);10             stock.Update();11             Console.ReadLine();12         }13     }14     public delegate void NotifyEventHandler(object sender);15     public class Stock16     {17         public NotifyEventHandler NotifyEvent;18         private String _symbol;19         private double _price;20         public Stock(String symbol, double price)21         {22             this._symbol = symbol;23             this._price = price;24         }25         public void Update()26         {27             OnNotifyChange();28         }29         public void OnNotifyChange()30         {31             if (NotifyEvent != null)32             {33                 NotifyEvent(this);34             }35         }36         public String Symbol37         {38             get { return _symbol; }39         }40         public double Price41         {42             get { return _price; }43         }44     }45     public class Investor46     {47         private string _name;48         public Investor(string name)49         {50             this._name = name;51         }52         public void SendData(object obj)53         {54             if (obj is Stock)55             {56                 Stock stock = (Stock)obj;57                 Console.WriteLine("Notified {0} of {1}'s " + "change to {2:C}", _name, stock.Symbol, stock.Price);58             }59         }60     }

效果及实现要点


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

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

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

 

转载于:https://www.cnblogs.com/Ming8006/archive/2013/03/01/2938252.html

你可能感兴趣的文章
Mac上首次出现word宏恶意软件,可窃取用户敏感数据
查看>>
jQuery常用事件方法详解
查看>>
【教程】如何创建尽可能小的Docker容器
查看>>
DockOne微信分享(一一六):某股份制商业银行定制化PaaS介绍
查看>>
被骗好多年:原来这才是大数据
查看>>
阿里云ApsaraCache的正式开源,为什么不能仅仅满足于商业上的成功?
查看>>
这书真的不错--Spring MVC Beginner's Guide
查看>>
如何追踪每一笔记录的来龙去脉:一个完整的Audit Logging解决方案[上篇]
查看>>
“前.NET Core时代”如何实现跨平台代码重用 ——源文件重用
查看>>
微信小程序自定义多功能模态对话框案例实战
查看>>
activity初探(基于kft-activiti-demo的一个小例子)
查看>>
比Wi-Fi快100倍的Li-Fi,这事靠谱吗?
查看>>
还在用软盘驱动器?下面来看看一款超越1.44 MB容量上限的新方案
查看>>
帮你搞定旅行大小事!旅游季就需要这三个NAS套件
查看>>
ONF推出第二版Atrium 获OpenDaylight支持
查看>>
Iodine:一个优秀的Java语言工具链
查看>>
戴尔开展独立软件供应商战略合作 发力云应用及大数据解决方案
查看>>
埃哲森:物联网市场潜力达万亿美元
查看>>
高德地图荣获2016金瑞奖“最具成长力产品奖”
查看>>
《数据冰山报告》显示,管理数据越来越重要
查看>>