对象配线的Observer-Observable模式

ZDNet软件频道 时间:2002-05-20 作者:BUILDER.COM |  我要评论()
本文关键词:
这篇文章阐述了一种具有可伸缩性的对象配线模式(针对.NET框架)。
在设计软件应用程序的时候往往需要实现应用程序内外对象之间可伸缩的通讯机制。当对象内出现某种状态或情况时你需要通知其他对象,反过来也是如此。这一过程通常被称做对象配线(object wiring),也就是把对象连在一起以便它们相互之间能够通信。这篇文章阐述了一种具有可伸缩性的对象配线模式(针对.NET框架)。
对象通知

通过编写对象通知矩阵(object notification matrix)代码的方式即可创建出实现以上行为的对象通知结构,对象通知矩阵指的是对象内部状态和在出现这些状态的情况下应得到通知的各个对象之间的映射。然而,直接编写对象通知矩阵对实现对象配线这一目标而言却是一种不但笨拙而且不具伸缩性的方式。

在日常的编程工作中会经常用到对象通知。在本文余下部分我用网络打印机这个概念为例。网络打印机维持着一个打印请求(打印任务)队列,这些请求来自客户机,打印机根据以下标准完成打印任务。比如:

  • 先来的任务先接受服务
  • 打印任务的优先级越高越先接受服务

假设网络打印机(在这个例子中就是PrintTaskController对象)因出现机械故障或其他问题而无法执行打印任务,那么网络打印机就应该设法通知打印队列中所有正在等待执行的PrintTask对象。接着,这些打印任务又应该通知提出请求的各个客户程序。在我们的例子中,PrintTaskController对象必须按顺序通知所有未被执行的PrintTask对象。

同步和异步通知

如果我们采取同步通信方式通知每一项任务,Printtaskcontroller的应答情况就将取决于PrintTask的反应了。例如,如果某一PrintTask对象被挂起,那么PrintTaskController只好无奈地一直等待下去而无法通知其他PrintTask对象。所以我们应该对这些对象采用松散耦合的方式来改进程序框架的响应性能。也就是建立异步事件通知机制。在异步事件通知期间,.NET框架采用不同的线程处理每个事件而不影响产生事件程序的应答。图A即显示了网络打印机的流程。

图A

网络打印机的流程,不同对象之间的通信

 

对象配线

实现通用的对象配线行为可以采用两种方法:

  • 如果某种对象必须观察其他对象的某种状态,那么称前者为观察员(Observer)。
  • 如果某种对象必须观察自身内部的某种状态并且通知其他向其注册的对象,那么我们称这种对象可见(Observable)。

在我们的例子中,因为PrintTask对象需要查看PrintTaskController对象上产生的事件所以PrintTask对象就是观察员;反过来PrintTaskController对象则可见。在这种设计框架下,所有的观察员对象都需要向可见对象注册以便收到通知。因为可见对象实际上根据某些具体条件发布事件,所以有些人也把可见对象称为发行人(publishers),再因为观察员对象注册了发行人发布的事件所以他们把观察员对象称为订户(subscribers)。

幸好,在各种场合下实现以上行为的通用方式都不太难,原因是.NET暴露了某些专为实现此类行为的关键词,用起来非常方便。.NET框架提供了两种有关对象:代表(delegate)和事件(event)。

代表

代表是一种保存方法引用的类。它在本质上等价于一种类型安全的函数指针或回调函数。但不同于其他类的是每一个代表类都有一个签名,它只能保存匹配这一签名的方法引用。

在我们的例子中,代表的作用就是作为所有注册观察员对象的容器。实际上观察员对象注册的是它们的回调函数(回调函数)方法。当可见对象进入某种状态时就会调用这些方法。每一个打印任务都必须注册它的回调函数方法(比如PrintTaskController对象的notifyClient ())。

事件

在使用事件对象编码的情况下,当某些事件出现之时就可以调用指定的代表。如果我们把代表定义为事件,那么C#编译器就会为这种事件代表(event delegate)类型添加某些操作符,例如 += 和 -= 等等。这些操作符用来增加或删除可见事件对象之内的回调函数方法。

可见类

在本文的这一节及以后部分,我们将讨论Observer-Observable模式的具体编程。这种模式能被用来实现对象之间的异步配线行为(事件通知)。我们在EventDelegate名称空间实现这种模式。

在可见类一方,我们定义了一个带签名的代表和一个delegate类型的事件。NotifyTasks是一个公共方法,负责通知向可见对象注册的所有对象。它通知对象是由调用OnObserved方法来完成的。OnObserved被实现为受保护的虚拟函数,这样扩展可见类的类就能实现它们自己的重载OnObserved方法了。

为了实现回调方法的异步调用,我们采用了.NET框架的BeginInvoke方法,并且把一个Async回调函数对象和一个函数指针传递给回调函数方法(请参考.NET帮助了解有关Async回调函数类和创建Async回调函数对象的更多信息)。大致说来,要创建Async回调函数对象就需要定义一个私有方法(我们的例子中就是AsyncReturn)并带有IAsyncResult 类型的参数。该方法在回调函数方法异步返回的时候将被.NET运行时调用。清单A所示为有关的程序代码。

观察员类

在这种模式下的观察员类回实现一个其签名必须匹配delegate签名的公共方法,而delegate则保存了对回调函数方法的引用。在这个方法的内部我们编写可见对象产生事件时相应的逻辑处理程序。程序代码请见清单B

控制器类

为了测试对象配线我们还实现了一个控制器(Controller)类。在这个类里我们首先创建了作为可见类的PrintTaskController对象;然后我们创建了作为观察员类的两个PrintTask对象。创建万这些对象之后,我们就向PrintTaskController的事件代表注册PrintTask的观察员方法。现在,万一打印机出现故障,PrintTaskController就会异步调用注册的回调函数方法。出于测试的目的,我们把printFailure设置为真。清单C显示相应的程序代码。

运行这个应用程序的时候使用了我们EventDelegate名称空间中刚才讨论的所有类,我们在Visual Studio .NET环境下对程序进行了编译,当然也可以通过csc命令以命令行的方式编译程序。

小结

这篇文章中阐述的Observer-Observable行为模式对实现应答程序而言是一种灵活、可扩展的稳固方案。你可以把这种设计方案应用在需要对象间配线或者事件通知的多种场合下。例如,类似的模式还广泛运用于用户界面编程、远程方法调用等领域。

责任编辑:炒饭

百度大联盟认证黄金会员Copyright© 1997- CNET Networks 版权所有。 ZDNet 是CNET Networks公司注册服务商标。
中华人民共和国电信与信息服务业务经营许可证编号:京ICP证010391号 京ICP备09041801号-159
京公网安备:1101082134