重温观察者模式

来源:互联网 发布:闲鱼钓鱼网站源码 编辑:程序博客网 时间:2024/05/19 00:54


观察者模式

1、概述

ObserverSubject互为耦合,但是这种耦合的双方都依赖于抽象,而不依赖于具体

场景概述

观察者模式是基于一对多的关系,该模式一般是应用于由一个目标对象和N个观察者对象,目标对象的状态发生改变或做出某种行为时,正在观察该目标对象的观察者们将自动地、连锁地作出相应的响应行为。

原理

我们可以把观察目标理解为主动方、发布方、主体、主题等;把观察者理解为被动方、订阅方、观察器、观察者等。目标是整个行为链的源头,其它观察者都依赖于它的变化而作出响应。为了实现低耦合,我们不能使用直接调用的方式而需要利用订阅(清单)-通知的机制去完成设计。通俗地说就是观察者向目标订阅它的改变,而目标发生改变后就通知所有已经订阅了它的改变的观察者,从而执行订阅的内容。这种机制的好处在于降低耦合度,分工明确,目标只负责在自身状态发生改变或做出某种行为时向自身的订阅清单发出通知,而不是直接调用观察者的行为(方法);观察者只负责向目标订阅它的变化,以及定义自身在收到目标通知后所需要做出的具体行为(也就是订阅的内容)。

2C#中的观察者模式

概述

每种编程架构及程序语言,对观察者模式都有不通的具体实现。在.NET框架中,C#语言使用委托以及事件,可以很好的实现观察者模式。委托相当于订阅清单的角色,当目标中关联了该委托的事件被触发时,则委托将自动按序执行观察者注册于委托中的方法。

模型与观察者基类

我们把观察者模式的参与者都描述为派生自模型及观察者二个抽象基类的类。模型规划了事件,而观察者则规划了订阅及行为。

模型需要做的只是声明委托以及声明委托类型的事件。当然,还可以附加上封装了触发委托事件的方法。所有派生自模型的类都是具体目标,它们所要做的只是在适当的场合触发事件。(即发出通知)。

在观察者基类中,我们通过构造器将抽象的响应方法注册(订阅)于委托事件中。所有派生自观察者基类的类都是具体观察者。因为订阅行为已经在抽象基类完成,具体观察者需要做的只是通过覆盖观察者基类的方法去定义具体需要响应的行为,和通过构造器把需要观察的具体目标传递给基类构造器。

示例1

通过对模型与观察者基类的分析可知,委托与事件的机制几乎消除了这两个模块之间的耦合,灵活性提高了很多。如果需要增加观察者,则只需要覆盖基类抽象方法及把观察目标传递给基类。

1.  建立模型(目标基类)

namespace DelegateEvent

 6

 7{

 8

 9   /**////<summary>

10

11   ///    Observer Pattern(观察者模式),此类作为所有Subject(目标)的抽象基类

12

13   ///所有要充当Subject的类(在此事例中为"")都继承于此类.

14

15   ///    我们说此类作为模型,用于规划目标(即发布方)所产生的事件,及提供触发

16

17   ///事件的方法.

18

19   ///    此抽象类无抽象方法,主要是为了不能实例化该类对象,确保模式完整性.

20

21   ///    具体实施:

22

23   ///    1.声明委托

24

25   ///    2.声明委托类型事件

26

27   ///    3.提供触发事件的方法

28

29   ///</summary>

30

31   publicabstractclass ModelBase

32

33   {

34

35       public ModelBase()

36

37       {

38

39       }

40

41       /**////<summary>

42

43       ///声明一个委托,用于代理一系列"无返回""不带参"的自定义方法

44

45       ///</summary>

46

47       publicdelegatevoid SubEventHandler();

48

49       /**////<summary>

50

51       ///声明一个绑定于上行所定义的委托的事件

52

53       ///</summary>

54

55       publicevent SubEventHandler SubEvent;

56

57

58

59       /**////<summary>

60

61       ///封装了触发事件的方法

62

63       ///主要为了规范化及安全性,除观察者基类外,其派生类不直接触发委托事件

64

65       ///</summary>

66

67       protectedvoid Notify()

68

69       {

70

71           //提高执行效率及安全性

72

73           if(this.SubEvent!=null)

74

75               this.SubEvent();

76

77               

78

79       }

80

81   }

82

83}

2.  建立观察者基类(单行为,多行为)

//--------------------单行为---------------------

复制代码

1using System;

 2

 3

 4

 5namespace DelegateEvent

 6

 7{

 8

 9   /**////<summary>

10

11   ///    Observer Pattern(观察者模式),此类作为所有Observer(观察者)的抽象基类

12

13   ///所有要充当观察者的类(在此事例中为"老鼠""")都继承于此类.

14

15   ///    我们说此类作为观察者基类,用于规划所有观察者(即订阅方)订阅行为.

16

17   ///    在此事例中,规划了针对目标基类(ModelBase)中声明的"无参无返回"委托的一个

18

19   ///方法(Response),并于构造该观察者时将其注册于具体目标(参数传递)的委托事件中.

20

21   ///    具体实施过程:

22

23   ///    1.指定观察者所观察的对象(即发布方).(通过构造器传递)

24

25   ///    2.规划观察者自身需要作出响应方法列表

26

27   ///    3.注册需要委托执行的方法.(通过构造器实现)

28

29   ///</summary>

30

31   publicabstractclass Observer

32

33   {

34

35       /**////<summary>

36

37       ///构造时通过传入模型对象,把观察者与模型关联,并完成订阅.

38

39       ///在此确定需要观察的模型对象.

40

41       ///</summary>

42

43       ///<param name="childModel">需要观察的对象</param>

44

45       public Observer(ModelBase childModel)

46

47       {

48

49           //订阅

50

51           //把观察者行为(这里是Response)注册于委托事件

52

53           childModel.SubEvent+=new ModelBase.SubEventHandler(Response);

54

55       }

56

57

58

59       /**////<summary>

60

61       ///规划了观察者的一种行为(方法),所有派生于该观察者基类的具体观察者都

62

63       ///通过覆盖该方法来实现作出响应的行为.

64

65       ///</summary>

66

67       publicabstractvoid Response();

68

69   }

3、多行为

3namespace DelegateEvent

 4

 5{

 6

 7   /**////<summary>

 8

 9   ///定义了另一个观察者基类.该观察者类型拥有两个响应行为.

10

11   ///并在构造时将响应行为注册于委托事件.

12

13   /// (具体描述请参照另一观察者基类Observer)

14

15   ///</summary>

16

17   publicabstractclass Observer2

18

19   {

20

21       /**////<summary>

22

23       ///构造时通过传入模型对象,把观察者与模型关联,并完成订阅.

24

25       ///在此确定需要观察的模型对象.

26

27       ///</summary>

28

29       ///<param name="childModel">需要观察的对象</param>

30

31       public Observer2(ModelBase childModel)

32

33       {

34

35           //订阅

36

37           //把观察者行为(这里是ResponseResponse2)注册于委托事件

38

39           childModel.SubEvent+=new ModelBase.SubEventHandler(Response);

40

41           childModel.SubEvent+=new ModelBase.SubEventHandler(Response2);

42

43           

44

45       }

46

47       /**////<summary>

48

49       ///规划了观察者的二种行为(方法),所有派生于该观察者基类的具体观察者都

50

51       ///通过覆盖该方法来实现作出响应的行为.

52

53       ///</summary>

54

55       publicabstractvoid Response();

56

57       publicabstractvoid Response2();

58

59   }

60

61}

4、具体目标

 

3namespace DelegateEvent

 4

 5{

 6

 7   /**////<summary>

 8

 9   ///    此类为观察者模式中的具体目标(即具体发布方),其继承于模型.

10

11   ///其中包含(调用)了在模型中被封装好的触发委托事件的方法.

12

13   ///</summary>

14

15   publicclass Cat:ModelBase

16

17   {

18

19       public Cat()

20

21       {

22

23       }

24

25       /**////<summary>

26

27       ///定义了猫的一种行为----大叫

28

29       ///</summary>

30

31       publicvoid Cry()

32

33       {

34

35           System.Console.WriteLine("Cat Cry..");

36

37           //调用了触发委托事件的方法.

38

39           //通知委托开始执行观察者已订阅的方法.

40

41           this.Notify(); 

42

43        }

44

45   }

5.  具体观察者

复制代码

 1using System;

 2

 3namespace DelegateEvent

 4

 5{

 6

 7   /**////<summary>

 8

 9   ///    此类为观察者模式中的具体观察者(即具体发布方),其继承于观察者基类.

10

11   ///其中覆盖了观察者基类规划好的方法,实现了响应的具体行为.

12

13   ///</summary>

14

15   publicclass Mouse:Observer

16

17   {

18

19       /**////<summary>

20

21       ///观察者可以拥有自己的成员(字段或者方法).

22

23       ///在此事例中增加了"老鼠的名字"

24

25       ///</summary>

26

27       privatestring name;

28

29       /**////<summary>

30

31       ///    构造时确定观察者所需要观察的对象(具体目标),并传递给观察者基类构造器,

32

33       ///实现响应行为(方法)的订阅.另外,为观察者实例初始化成员.

34

35       ///</summary>

36

37       ///<param name="name">老鼠的名字</param>

38

39       ///<param name="childModel">

40

41       ///    需要观察的对象(发布方).

42

43       ///    此处用模型基类来传递,是为了兼容所有派生于此模型的观察者,从而提高扩展性.

44

45       ///</param>

46

47       public Mouse(string name,ModelBase childModel):base(childModel)

48

49       {

50

51           //初始化字段(老鼠的名字)

52

53           this.name=name;        

54

55       }

56

57       /**////<summary>

58

59       ///覆盖了该类观察者需要作出的具体响应行为.

60

61       ///此行为已在观察者基类中注册于委托事件,由委托事件调度执行,不需要直接调用.

62

63       ///</summary>

64

65       publicoverridevoid Response()

66

67       {

68

69           //具体响应内容

70

71           System.Console.WriteLine(this.name+"开始逃跑");

72

73       }

74

75

76

77   }

6、具体观察者(主人)

复制代码

 1using System;

 2

 3namespace DelegateEvent

 4

 5{

 6

 7   /**////<summary>

 8

 9   ///    此类为观察者模式中的具体观察者(即具体发布方),其继承于观察者基类.

10

11   ///其中覆盖了观察者基类规划好的方法,实现了响应的具体行为.

12

13   ///</summary>

14

15   publicclass Master:Observer

16

17   {

18

19       /**////<summary>

20

21       ///    构造时确定观察者所需要观察的对象(具体目标),并传递给观察者基类构造器,

22

23       ///实现响应行为(方法)的订阅.

24

25       ///</summary>

26

27       public Master(ModelBase childModel):base(childModel)

28

29       {

30

31       }

32

33

34

35       /**////<summary>

36

37       ///覆盖了该类观察者需要作出的具体响应行为.

38

39       ///此行为已在观察者基类中注册于委托事件,由委托事件调度执行,不需要直接调用.

40

41       ///</summary>

42

43       publicoverridevoid Response()

44

45       {

46

47           System.Console.WriteLine("主人醒来");

48

49       }

50

51   }

52

53}

7、具体观察者(宝宝)

 

复制代码

 1using System;

 2

 3namespace DelegateEvent

 4

 5{

 6

 7   /**////<summary>

 8

 9   ///    此类为观察者模式中的具体观察者(即具体发布方),其继承了订阅了2个响应行为的

10

11   ///观察者基类.

12

13   ///    其中覆盖了观察者基类规划好的二个方法,实现了响应的具体行为.

14

15   ///</summary>

16

17   publicclass Master2:Observer2

18

19   {

20

21       /**////<summary>

22

23       ///    构造时确定观察者所需要观察的对象(具体目标),并传递给观察者基类构造器,

24

25       ///实现响应行为(方法)的订阅.

26

27       ///</summary>

28

29       public Master2(ModelBase childBase):base(childBase)

30

31       {

32

33       }

34

35

36

37       /**////<summary>

38

39       ///覆盖了该类观察者需要作出的具体响应行为.

40

41       ///此行为已在观察者基类中注册于委托事件,由委托事件调度执行,不需要直接调用.

42

43       ///</summary>

44

45       publicoverridevoid Response()

46

47       {

48

49           Console.WriteLine("baby醒来。。。。");

50

51

52

53       }

54

55       /**////<summary>

56

57       ///覆盖了该类观察者需要作出的另一个响应行为.

58

59       ///</summary>

60

61       publicoverridevoid Response2()

62

63       {

64

65           Console.WriteLine("开始哭闹。。。。。");

66

67       }

68

69   }

70

71}

72

8、运行测试

1using System;

 2

 3namespace DelegateEvent

 4

 5{

 6

 7   /**////<summary>

 8

 9   /// Observer Pattern(观察者模式)事例分析

10

11   ///

12

13   ///题目:猫大叫,两只老鼠开始逃跑,主人醒来,宝宝也醒来了并且哭了起来.

14

15   ///

16

17   ///关于目标(发布方):

18

19   ///    在此事例中,只有一个目标对象(发布方),因为其他全部实体的行为都是

20

21   ///响应它的"大叫"所执行的.猫是主动方,它的大叫引起一系列的连锁反应.

22

23   ///

24

25   ///关于观察者(订阅方):

26

27   ///    至于此事例的中的观察者分别有两大类,一类是听到猫大叫后只作出一种

28

29   ///反应的观察者(老鼠,主人),另一类是听到锚大叫后会作出两种响应的观察者(

30

31   ///宝宝).所以观察者分别需要派生于两个不同的观察者基类.

32

33   ///</summary>

34

35   publicclass SubMain

36

37   {

38

39       public SubMain()

40

41       {

42

43           

44

45       }

46

47       publicstaticvoid Main()

48

49       {

50

51           //声明并实例化一个目标(即发布方)对象----

52

53           Cat myCat=new Cat();

54

55           //声明并实例化一个Mouse类型的观察者对象--名叫mouse1的老鼠.并把那只猫作为它所要观察的对象.

56

57           Mouse myMouse1=new Mouse("mouse1",myCat);

58

59           //类似地生成另一只名叫mouse2的老鼠(观察者),把同一只猫作为它的观察的对象.

60

61           Mouse myMouse2=new Mouse("mouse2",myCat);

62

63           //声明并实例化一个Master类型的观察者--主人,并同时把那只猫也作为他的观察对象.

64

65           Master myMaster=new Master(myCat);

66

67           //声明并实例化一个Master2类型的观察者--宝宝,同时把那只猫也

68

69           Master2 myLittleMaster=new Master2(myCat);

70

71

72

73           //猫大叫,并触发了委托事件,从而开始按顺序调用观察者已订阅的方法.

74

75           myCat.Cry();

76

77

78

79           Console.Read();

80

81       }

82

83   }

 

示例2

//“观察者接口   
public interface IObserver {   
   void Notify(object anObject);   
}   
   
//“
被观察对象接口   
public interface IObservable {   
   void Register(IObserver anObserver);   
   void UnRegister(IObserver anObserver);   
}   
 
观察者和被观察对象都分别从这两个接口实现,所有的操作都是由这两个接口定义的,而不是具体的实现。所以观察者和被观察对象没有绑定在一起。我们可以方便的更改观察者和被观察对象的任意部分而不影响其他部分。   
 
下面实现具体的被观察对象。下面的类是所有被观察对象的基类,实现了所有被观察对象都必须的方法。我们使用一个Hashtable作为观察者的容器。代码如下:   
    
 
//
所有被观察对象的基类   
public class ObservableImpl : IObservable {   
         
   //
保存观察对象的容器   
   protected Hashtable _observerContainer = newHashtable();   
      
   //
注册观察者   
   public void Register(IObserver anObserver){   
     _observerContainer.Add(anObserver,anObserver);    
   }   
         
   //
撤销注册   
   public void UnRegister(IObserver anObserver){   
     _observerContainer.Remove(anObserver);    
   }   
   
   //
将事件通知观察者   
   public void NotifyObservers(object anObject){    
      //
枚举容器中的观察者,将事件一一通知给他们   
      foreach(IObserver anObserver in_observerContainer.Keys) {    
        anObserver.Notify(anObject);    
      }   
   }   
}   
 
上面的类不是最终要实现的被观察对象,而是所有被观察者的基类,其中实现了所有观察对象共有的功能。这个类可以干脆定义为abstract,使得程序员不可以创建其实例。接口以及实现这个接口的虚类既保持了类之间松散的耦合,又使多个具体实现可以使用相同的功能。   
 
下面最终实现观察者模式,使用用户界面——业务数据作为例子:   
    
 
//
业务数据(被观察对象)   
public class SomeData : ObservableImpl {   
   //
被观察者中的数据   
   float m_fSomeValue;   
   
   //
改变数据的属性   
   public float SomeValue {   
      set {   
         m_fSomeValue =value;   
        base.NotifyObservers(m_fSomeValue);//
将改变的消息通知观察者   
      }   
   }   
}    
//
用户界面(观察者)   
public class SomeKindOfUI : IObserver {   
   public void Notify(object anObject){    
      Console.WriteLine("The new value is:"+ anObject);    
   }   
}   
   
//
实际调用的过程   
public class MainClass{   
   public static void Main() {   
      //
创建观察者和被观察者   
      SomeKindOfUI ui = newSomeKindOfUI();   
      SomeData data = newSomeData();   
   
      //
在被观察对象中注册观察者   
      data.Register(ui);   
   
      //
改变被观察对象中的数据,这时被观察者会通知观察者   
      data.SomeValue = 1000f;   
   
      //
注销观察者,停止观察   
     stock.UnRegister(stockDisplay);   
   }   
}  

总结

在事件的模式下,声明事件的类就是被观察者。被观察者不需要实现对观察者的注册,只需要公开一个事件,而不实行任何操作。被观察者也不需要将自己注册到观察对象中,而是要创建一个特定的代理的实例,将这个代理绑定到某个方法上。用这样的方式注册或者撤销观察者对观察对象的观察。仔细研究代理和事件的模式就不难发现,IObserverIObservable接口的方法可以减少观察者和观察对象之间的耦合,而代理和事件几乎消除了这两个模块之间的耦合,灵活性提高了很多。  


0 0