范振钧,巩小东,王增强
(通化师范学院 计算机学院,吉林 通化134002)
设计模式就是对象(类 )的集合,它具有解决某一类特定问题的功能,而且是解决这类问题的最优方案[1].应用设计模式可以使软件具备良好的可靠性、可扩展性、可复用性和可维护性.
观察者模式是设计模式中的经典模式,它定义了对象间的一对多依赖关系.当一方的对象改变状态时,所有的依赖者都会被通知并自动被更新[2].在观察者模式中,被依赖的一方叫做目标或主题(Subject),依赖方叫做观察者(Observers).观察者模式可以让多个观察者同时监听同一个主题,当主题对象发生变化时,通知所有观察者,使各个观察者能作出相应的响应.主题和观察者之间是松耦合,主题可随时增加或减少观察者而自身不受影响.观察者根据主题状态的改变,对自身的行为和数据做相应的变化,这种变化对主题没有任何影响.使用观察者模式主要目的就是减少组件间耦合度,减少对象之间的依赖关系.
在Observer 模式中有3 种基本的操作, 即注册、通知和注销.
(1)注册.观察者类调用主题的注册方法,在主题类中登记.
(2)通知.当主题的数据变化时,主题向注册的观察者发送消息.
(3)注销.当观察者不需要继续观察主题,执行主题类的注销操作,解除了对主题的观察.
传统方法一般都采用接口的方法.抽象观察者类提供了与主题中的数据变化相适应的更新方法,所有观察者类都要实现这个接口,用来实现观察者自身行为或外观的变化.主题接口提供了注册、注销以及通知操作等三个抽象方法,充当主题的类必须实现这三个方法.传统的观察者实现代码如下:
Public interface iRaditionalObserver // 观察者接口
{void ChangeUpdate(object anobject) //观察者更新方法 }
//主题接口
Public Interface Subject
{ void Logon(RaditionalObserver anObserver);//注册}
void logout(IRationalObserver anObserver);//注销 }
//观察者实例
public class Observer:iRaditionalObserver
{ public void ChangeUpdate (object anObject)
{ //观察者接口中的更新方法, 实现自身外观或行为的改变 } }
//主题实例
public class RaditionalSubject:Subject
protected Hashtable observerContainer= new Hashtable( ) ;//在主题类中有个列表,作为观察者的容器来存放观察者
public void logon( RaditionalObserver anObserver)
{ observerContainer.Add(RaditionalObserver anObserver) //注册 }
public void logout( RaditionalObserver anObserver)
{ observerContainer.Remove(anObserver) //注销 }
public void Notify(Object anObject)
{ foreach(RaditionalObserver anObserver in ObserverContainer)
{ anObserver.Notify(anObject) ;//观察者链表的循环访问通知.}
在上述观察者模式实现方法中,抽象出观察者接口后,目标和观察者就只是在抽象层面上耦合,也就是说目标只是知道观察者接口,并不知道具体的观察者的类,从而实现目标类和具体的观察者类之间解耦,完成了观察者模式的主要功能.
具体主题类RaditonalSubject中包含了一个私有的哈希对象obserContainer,它是一个保存观察者对象的容器.主题类中通过logon方法将观察者加入到哈希表中实现注册操作,通过logout方法撤销观察者和主体的联系,实现注销操作,当主题的状态变化时,通过Notify方法遍历哈希表中的所有观察者,通知其数据的改变,实现通知操作.在传统方法中存在着如下缺点:
(1)主题类要保存观察者的列表,从而实现注册、注销等服务,所以观察者和观察者之间还存在着间接的依赖关系.
(2) 如果一个被观察者对象有很多的观察者的话,通知会花费很多时间.因为存在着观察者的列表,存在着列表的循环遍历,通知的效率取决于列表的规模,当观察者的数目很大时,容易形成拥塞.
为了在Observer 模式中实现进一步的低耦合,可以利用.net提供的委托和事件技术实现Observer 模式[3].
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用选择语句,使程序具有更好的可扩展性.委托可以不知道自己封装方法所属类的详细信息,只要调用委托的方法的参数类型和返回类型与委托的相匹配,该方法就可以被调用.我们可以充分利用委托的这一重要属性,结合事件机制实现观察者模式,进一步地降低对象间的耦合性,将静态的组合关系变为运行时的动态组合关系.
委托的另一个重要的属性是多播.通过多播属性可以实现观察者的注册和注销.
在.net平台中,事件就是对委托的进一步封装,事件是当对象发生某些变化时,用于向该类的客户提供通知的一种结构.事件基于委托,为委托提供一种发布订阅机制[4],事件可以简单地分成两个部分,事件发生的类和事件接收处理的类.事件发生的类就是在这个类中触发了一个事件,但这个类并不知道哪个对象或方法将会收到并处理它触发的事件,触发事件时调用的处理程序方法需要定义,其参数由委托类型来定义.事件发生的类就可以看做主题,事件接收处理的类作为观察者.发送方和接收方之间可以通过委托作为媒介,来完成事件的处理.
(1)实现方案.①主题类的设计.在主题类中建立一个代理, 将更新函数的签名传递给这个代理,然后在主题类中用这个代理声明一个事件,定义事件触发机制.②观察者类的设计.观察者类需要提供一个更新函数,确保该更新函数的签名和代理的签名一致.③注册和注销操作.在程序运行时,观察者类通过+=操作将更新函数注册到主题类中,当观察者不需要观察主题对象时,可以从委托中删除方法调用实现注销操作.
(2)具体实现例子.通过一个简单的实例来说明.net下观察者模式的实现过程.在一个股票推荐的网站中,当注册的投资者所投资的股票价格在股票市场发生变化时,投资者可以自动得到通知.
//声明委托作为事件与观察者的中介
Public delegate void delegateUpdate(double price)
//定义主题类
class Stock
{ Public event delegateUpdate handlerPriceChange; //引入事件,使用委托作为事件代理
Protected double price; //股票价格
Public double Price // Properties
{set //写属性
{price=value;
if(handlerPriceChange!=null)//事件引发,通过委托发给观察者
delegateUpdate(value);
}
}
class Investor//观察者角色
{public void Update(double price)//和委托的签名必须相同
{Console.WriteLine("Price is changed {0}",price);}
|
主程序:
Stock ibm=new stock();//主题类实例
Investor s=new Investor();//观察者实例
Ibm.HandlerPriceChange+=s.Update; //注册观察者s
ibm.HandlrPriceChange+=b.Update; //注册观察者b
//Change price,which notifies investors
ibm.Price=121.00;//主题类触发事件,自动通知所有注册的观察者
Ibm.HandlerPriceChange-=s.Update; //注销s
Ibm.HandlrPriceChange-=b.Update; //注销b
在上述代码中,主题类和观察者类都没有实现任何接口,主题类只需提供一个代理和一个事件,就能将数据变化的信息逐一发送到每个观察者对象,并且还能更加容易的增加新的观察者对象,根本不需要考虑它从何处继承而来,也不需要统一他们的接口调用方法.观察者类只需要实现针对主题变化的更新方法,并把该方法注册到主题类中提供的事件中去,当观察者不需要观察主题类中的变化时,通过-=操作实现注销功能.通过代理和事件机制,改进了传统方法中的链表及相应的遍历操作带来的缺陷,将静态的组合关系转变为运行时的动态组合关系,由于没有使用接口,提高了系统的灵活性.
在.net平台下的观察者模式实现方案中,注册、注销、通知三种基本操作可以通过委托结合事件机制动态进行,解除了目标对象对观察者的依赖,使目标与观察者的藕合性更低,增加了设计的灵活性.
参考文献:
[1]林碧英,曲俊华.设计模式在电子商务交易网站中的应用[J].计算机系统应用,2005(1).
[2]Erich Gamma, Richard Helm, Ralph Johnson.设计模式:可复用面向对象软件的基础[M].李英军,马晓星,蔡敏,等译.北京: 机械工业出版社,2005.
[3]喻金平,刘孝会.C#中的委托在Observer 设计模式中的应用研究[J].软件导刊,2010,9(11).
[4]ChrisTlan Nagel .C#高级编程[M].北京:清华大学出版社,2011.