设计模式:观察者模式(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模式。现在有这样两个类: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 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和具体的通知对象之间依赖。


 

 
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 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,况且公司还会有很多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 : 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则扮演了主题对象角色,在任何时候,只要调用了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 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接口更为松耦合的设计。

 

posted @ 2013-03-01 10:48  明-Ming  阅读(225)  评论(0编辑  收藏  举报