代码改变世界

观察者模式

2012-05-29 20:42  刘永强  阅读(193)  评论(0)    收藏  举报
•观察者模式
•观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
 

观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察对象。在刚才的例子中,业务数据是被观察对象,用户界面是观察者。观察者和被观察者之间存在“观察”的逻辑关联,当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应。如果在用户界面、业务数据之间使用这样的观察过程,可以确保界面和数据之间划清界限,假定应用程序的需求发生变化,需要修改界面的表现,只需要重新构建一个用户界面,业务数据

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 = new Hashtable();

   

   //注册观察者

   public void Register(IObserver anObserver)

      

   //撤销注册

   public void UnRegister(IObserver 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 = new SomeKindOfUI();

      SomeData data = new SomeData();

 

      //在被观察对象中注册观察者

      data.Register(ui);

 

      //改变被观察对象中的数据,这时被观察者会通知观察者

      data.SomeValue = 1000f;

 

      //注销观察者,停止观察

      stock.UnRegister(stockDisplay);

   }

}

 

 

.NET中更好的实现方式

 

上面的形式是我们用一种最基本的方式实现了观察者模式,我们为观察者模式开发了一种特定的类型。在.NET框架中,使用代理以及事件,可以更好的实现观察者模式。C#中代理和事件的介绍可以看这一篇文章:在C#中使用代理的方式触发事件,里面有对代理和事件的详细描述,还有例程,这里就不多说了。在.NET支持的其他语言中也有各自的实现方式。

 

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