Pennant的日常
分享工作上的点点滴滴

2.3截取器(Interceptor)

1.问题

      框架,应用服务器和针对具体领域的软件架构,不能预先知道它们必须提供给用户的所有服务。使用最初设计时并不支持的新服务来扩展某些类型的框架也会是不灵活的。类似地,依靠应用程序自己实现所有必需的服务常常是不理想的,因为这样做破坏了复用的许多好处。因此,框架开发人员必须处理下列三种强制条件:

      1)在不需修改核心架构的情况下,架构应允许集成附加服务。

      2)把与应用有关的服务集成到框架中,应不影响现有的框架组件,也应不需要对使用框架的现有应用程序的设计或实现做改变。

      3)使用框架的应用程序也许需要监视和控制框架的行为。

 

2.解决方案

      通过预定义的接口向框架注册“带外”服务,从而允许应用程序透明地扩展起来,当某些事件发生时让框架自动地触发这些服务。

      由框架处理的指定的事件集,指明或者揭示一个截取器回调接口。应用程序能从这个接口派生具体截取器,以应用特定的方式实现处理这些事件并发的带外服务。为每个截取器提供一个分配器,它允许应用程序向框架注册它们的具体截取器。当指定的事件发生时,框架通知合适的分配器调用注册的具体截取器的回调。

      定义语境对象以允许具体截取器内省(introspect)和控制框架的内部状态和响应事件的行为的某些方面。语境对象提供访问和修改框架内部状态的方法,因而开放它的实现。当框架分配语境对象时,能把它们传递给具体截取器。

 

3.结构

      具体框架(Concrete framework)实例化一个一般的,可扩展的体系结构,来定义由特殊系统提供的服务。

     

 

      截取器与具体框架揭示的特殊事件或者事件集相关。截取器定义钩子方法的特征描述,当相应的事件发生时,具体框架将通过一个指定的分配机制自动地调用这些钩子方法。具体截取器对截取器接口特例化并实现它们的钩子方法,以针对应用处理这些事件。

     

 

      为了允许截取器处理特殊事件的发生,用一个具体框架定义分配器,用于配置和触发具体截取器。一般每个截取器有一个分配器。分配器定义注册和删除方法,应用程序使用它向具体框架预订和解预订具体截取器。

      分配器也定义另一接口,当具体截取器注册的指定事件发生时,具体框架调用此接口。当具体框架通知分配器这样一个事件发生时,分配器调用所有向它注册的具体截取器回调。分配器在一个容器中维护它的所有已注册的截取器。

      具体截取器可以使用语境对象访问和控制具体框架的某些方面。语境对象能提供存取器方法(accessor method)从具体框架中获取信息,并提供变异器方法(mutator method)控制具体框架的行为。具体框架可以实例化语境对象,并把它和每个回调调用一起传递给具体截取器。在这种情况下,语境对象能包含与触发它的创建的事件有关的信息。相反,当语境对象向分配器注册时,它能被传递给截取器。这种设计提供较少信息,但同时也导致较少开销。

     

 

      应用程序在具体框架上运行并复用它提供的服务。应用程序也能实现具体截取器并把它们向具体框架注册以处理某些事件。当这些事件发生时,它们触发具体框架及其分配器,去调用具体截取器回调,后者执行针对具体应用的事件处理。

     

 

3.实现

      1)如果还没有这样的模式的话,那么使用状态机或者等价的表示法来建立具体框架内部行为的模型。这种建模不必捕获具体框架的所有抽象,但应记载与截取有关的特性。

      2)标识和对截取点建模。

        2.1)标识具体框架状态变迁。此变迁对外部应用程序而言也许是不可见的,但它隶属于截取。例如,客户机也许想截取输出请求,这样它就能动态地增加类似登录或改变某些请求参数这样的功能。我们把这些状态变迁称为“截取点”(interception point)。

        2.2)把截取点划分为阅读器和记录器集。阅读器集(reader set)包括其中的应用程序仅从具体框架访问信息的所有状态变迁。相反,记录器集(writer set)包括其中的应用程序能修改具体框架行为的所有状态变迁。

        2.3)把截取点集成到状态机模型。通过引入中间状态,可以在状态机中对截取点建模。如果状态变迁取决于截取,那么在最初变迁的源状态和汇状态之间放置一个新的截取状态。这个截取状态触发相应的截取器。对于属于记录器集的截取点,我们引入附加的状态变迁,采用如下属性:

        ·截取状态是开始节点。

        ·目标节点是代表截取之后的具体框架后续行为的状态。

        2.4)把截取点划分到不相交的截取组。为了处理事件,具体框架常常执行一系列相关的活动,每个活动可以和一个截取点相关联。为了强调每个活动之间的关系,把一系列语义相关的截取点合并到一个截取组(interception group)或许是有用的。

      3)指定语境对象。语境对象允许截取器访问和控制框架的内部状态及其对特定事件的响应行为。

        3.1)确定语境对象语义。语境对象提供有关截取点的信息,它也可以定义控制框架后续行为的服务。

        3.2)确定语境对象类型的数目。以下两种策略用于选择语境对象的数目和类型:

        ·多接口(multiple interface)。如果在具体框架中的截取点包含多种需求,那么可以为不同的截取点定义不同类型的语境对象。这个策略是灵活的,因为它允许对特殊截取点的细粒度控制。然而它增加了具体截取器的开发人员必须理解的接口的数目。

        ·单接口(single interface)。指定具有单接口的通用语境对象是可能的。使用单接口减少语境对象接口的数目,但也会产生一个膨胀的、复杂的语境对象接口。

        3.3)定义如何将语境对象传递给具体截取器材。语境对象由具体框架实例化,并且通过下列两种策略之一传递给具体截取器:

        ·按注册(per-registration)。在这种策略中,当语境对象向分配器注册时,向截取器传递一次语境对象。

        ·按事件(per-event)。在这种策略中,每次回调调用时语境对象一起被传递给具体截取器。

        “按事件”策略允许具体框架提供有关特定事件发生的细粒度信息。相反,“按注册”策略仅提供特定事件类型的所有发生所共有的一般信息。然而,由于语境对象的重复创建和删除,“按事件”策略会招致更大的开销。

      4)指定截取器。截取器定义通用的接口,当截取点被触发时,具体框架通过分配器使用该接口调用具体截取器。

      5)指定分配器。为每个截取器定义一个分配器接口,应用程序可以使用该接口向具体框架注册和删除具体截取器。

        5.1)指定截取器注册接口。

        5.2)指定分配器回调接口。当一个截取事件发生时,具体框架通知它的分配器。当分配器得到通知后,它就调用其已注册的具体截取器的相应钩子方法。对于相关联的截取器提供给分配器的具体框架,分配器常常提供同一个接口。这种相似性有两种理由:

        ·通过允许分配器在不传输任何参数的情况下,把事件通知有效地委托给它的注册截取器,使性能更具效率。

        ·如果分配器的公共接口改变了,它能局部化和最小化所需的修改,这种修改可能是增加新的截取点到与分配器回调接口相关联的截取组。在这种情况下,会增加一个额外的钩子方法给回调接口。

      6)在具体框架中实现回调机制。当截取事件出现时,具体框架通知相应的分配器,接着分配器依次调用所有被注册的具体截取器回调的钩子方法。

      7)实现具体截取器。具体截取器可以从截取器派生,并且针对具体应用实现相应的截取器接口。

 

5.结论

优点:

      1)可扩展性和灵活性。通过定制和配置截取器和分配器接口,具体框架的用户能够在不改变具体框架体系结构或者实现的情况下,增加、修改和删除服务。

      2)事务分离。在不影响现有应用程序代码的情况下,可以透明地增加截取器,因为截取器被从应用程序行为中分解出来。

      3)支持对框架的监视和控制。截取器和语境对象有助于从具体框架中动态地获取信息,也有助于控制它的行为。

      4)层对称性。为实现分层的服务,开发人员可以为具体框架所提示的相关事件引入对称的截取器。

      5)可复用性。通过从其他应用程序代码中分离出截取器代码,可以跨应用地复用截取器。

 

不足:

      1)使设计问题复杂化。对使用特定具体框架的应用程序的需求预期并不容易,从而难以确定提供哪个截取器分配器。

      2)恶意的或易出错的截取器。如果一个具体框架调用截取器,而截取器未能返回,那么整个应用程序会阻塞。为了防止阻塞,具体框架可以使用可配置的超时值。然而这种方法会使具体框架的设计复杂化。

      3)潜在的截取级联。如果截取器应用语境对象以改变具体框架的行为,那么它也许会触发新的事件,从而启动底层状态机中的状态转换。截取级联会导致严重的性能瓶颈或者死锁。

 

posted on 2012-09-07 22:21  汝熹  阅读(390)  评论(0编辑  收藏  举报