设计模式:观察者模式(Observer Pattern)
作者:TerryLee 创建于:2006-07-17 出处:http://www.cnblogs.com/Terrylee/archive/2006/10/23/Observer_Pattern.html 收录于:2006-10-23 收录于:2013-03-01
结构图
意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
适用性
- 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
实现代码
下面通过一个例子来说明Observer模式。监控某一个公司的股票价格变化,可以有多种方式,通知的对象可以是投资者,或者是发送到移动设备,还有电子邮件等。一开始我们先不考虑Observer模式,通过一步步地重构,最终重构为Observer模式。现在有这样两个类:Microsoft和Investor,如下图所示:
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 Investor 12 { 13 get { return _investor; } 14 set { _investor = value; } 15 } 16 public String Symbol 17 { 18 get { return _symbol; } 19 set { _symbol = value; } 20 } 21 public double Price 22 { 23 get { return _price; } 24 set { _price = value; } 25 } 26 } 27 public class Investor 28 { 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 Program 41 { 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和具体的通知对象之间依赖。
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 Microsoft 23 { 24 private List<IObserver> observers = new List<IObserver>(); 25 private String _symbol; 26 private double _price; 27 public String Symbol 28 { 29 get { return _symbol; } 30 set { _symbol = value; } 31 } 32 public double Price 33 { 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 Program 58 { 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,况且公司还会有很多IBM,Google等,解决这样的问题很简单,只需要再对Microsoft类做一次抽象。如下图所示:
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 : IObserver 10 { 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 Stock 22 { 23 private List<IObserver> observers = new List<IObserver>(); 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 Symbol 47 { 48 get { return _symbol; } 49 } 50 public double Price 51 { 52 get { return _price; } 53 } 54 } 55 public class Microsoft : Stock 56 { 57 public Microsoft(String symbol, double price) 58 : base(symbol, price) 59 { } 60 } 61 //客户端代码 62 class Program 63 { 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则扮演了主题对象角色,在任何时候,只要调用了Stock的Update()方法,它就会通知它的所有观察者对象。同时可以看到,通过Observer模式,取消了直接依赖,变为间接依赖,这样大大提供了系统的可维护性和可扩展性。
.NET中的Observer模式
在.NET中,相信大家对于事件和委托都已经不陌生了,这里就不具体多说了。利用事件和委托来实现Observer模式我认为更加的简单和优雅,也是一种更好的解决方案。因为在上面的示例中我们可以看到,虽然取消了直接耦合,但是又引入了不必要的约束(暂且这么说吧)。即那些子类必须都继承于主题父类,还有观察者接口等。网上有很多这方面的例子,上面的例子简单的用事件和委托实现如下,仅供大家参考:
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 Stock 16 { 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 Symbol 37 { 38 get { return _symbol; } 39 } 40 public double Price 41 { 42 get { return _price; } 43 } 44 } 45 public class Investor 46 { 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接口更为松耦合的设计。