现在的位置: 首页 > 综合 > 正文

基于CAB和SCSF设计智能客户端(三)翻译

2012年06月25日 ⁄ 综合 ⁄ 共 4358字 ⁄ 字号 评论关闭

基础服务

前文曾经提到过,基础服务封装的是针对所有模块(或一些模块)和装载到智能客户端的组件共同使用的功能。和基础Shell不同的是,这些服务不一定和UI相关。它们封装了客户端的逻辑(也许是客户端的业务逻辑或者离线功能)。CAB提供了一些基础服务,除此之外,类似权限服务或者根据指定位置的配置文件加载模块的服务都是CAB没有提供的。当然,你可以引进你自己的服务。如下所示:

权限服务(CAB和SCSF没有提供,但是BankBranchWorkbench项目提供了这种服务,并证明了如何使用ActionCatalog来实现);

Web service代理,对Web services的调用和脱机工作能力进行了封装;

上下文服务,越过WorkItem来管理用户上下文;

获取应用程序核心配置的配置服务;

部署服务,为了程序的更新和自动升级在后台使用ClickOnce技术来部署。

引进通用服务首先需要定义接口,因为CAB允许你基于接口类型注册服务。如下所示:

MessageDisplayService svc=newMessageDisplayService(_rootWorkItem);

_rootWorkItem.Services.Add<IMessageDisplayService>(svc);

基础服务需要被封装到单独的模块,并且需要在装载包含实际用例的模块之前装载。

识别WorkItems

WorkItem负责封装用户想要通过应用程序完成的功能业务。本质上说,WorkItems是复杂智能客户端中最为重要和核心的部分,因为它直接提供实际的业务功能给用户。因此,一个WorkItem本身就包含或者可以获取到一个或几个SmartParts,以及相关的controller和model。WorkItem知道什么时候需要显示SmartPart,或者启动Sub-WorkItems,它通过SmartParts和Sub-WorkItems来管理状态。每一个CAB应用程序都有一个RootWorkItem作为进入模块中所有服务和其他WorkItem的入口。通过SCSF,每个模块都自动加载一个ModuleController标记的WorkItem,即RootWorkItem。文中其他部分提到的WorkItem,无论是直接添加的或是RootWorkItem或是带有ModuleController标记的WorkItem,都把它们归入first-level
WorkItem。它们是进入具体业务的入口。

一般来说,有两种方式来识别WorkItem:用例驱动方式和业务对象驱动方式。

用例驱动方式

CAB最大的优势就是允许你基于用例图来设计WorkItems。实际上,大多数情况下,用例和WorkItem是一一对应的,一个用例被封装到一个WorkItem中。因此,WorkItem本质上只是一个管理者,为了完成用例对应的功能实现必要的UI需求,并把用例相关的所有部分都放在一起。看图5,这个用例图是为一个股票管理系统设计的,它被用于帮助顾客进行股票操作,完成股票交易相关的需求。

假定你需要创建一个银行职员使用的智能客户端应用程序。这个应用程序帮助前台职员和顾客交流,帮助后台职员完成股票交易。首先你要将CAB中的WorkItem和用例一一对应起来。用例之间的关系有两种:父子关系和使用关系。当然,一个子用例且不被其他用例使用就会产生Sub-WorkItem。纯粹的子用例对应的Sub-WorkItem不能被它们Parent-WorkItem以外的WorkItem获取;被其他用例(包括父用例)使用的用例既可以直接被获取,也可以通过它们的Parent-WorkItem来获取。

根用例的角色

根用例产生了一级WorkItem(如果只有一个,它们可以直接通过ModuleController实现),它是进入Sub-WorkItem的入口。一级WorkItem负责管理所有Sub-WorkItem的状态和提供所有Sub-WorkItem正确的上下关系。对于一级WorkItem,在加载后推荐完成以下步骤:

一组WorkItem需要的服务;

获取本WorkItem需要的服务引用;

注册用于启动Sub-WorkItem的命令;

增加UIExtensionSite

根据需要实例化Sub-WorkItem

根据需要实例化SmartPart

一般来说,不包含Sub-WorkItem的简单WorkItem或者Sub-WorkItem本身都需要执行同样的步骤,除非它们没有注册服务和UIExtensionSite。此外,当Sub-WorkItem通过它们的父WorkItem来调用时,不能注册命令。

一级WorkItem是被包含它们的模块中的ModuleInit创建的。不管它们是不是自动创建,ModuleInit都会为了启动封装在它们内部的功能注册命令和UIExtensionSite。

例外的用例

让我们更仔细的看用例图,你会发现并不是所有的用例都会产生WorkItem。先看一看“要求联系银行”这个用例。首先记住我们是要为银行员工开发软件。一个顾客需要咨询股票(通过“要求联系银行”这个功能),他可能直接跑到前台或者使用网络解决方案,或者其他一些方式。但是,不管怎样,这个用例对于银行员工来说是没什么具体功能需要整合的。取而代之,“顾客识别”恰恰是需要考虑的一部分。

另外一个例子是“离线会商”用例。这对于你的智能客户端意味着什么?它是否应该放在一个单独的WorkItem中?它会改变业务逻辑吗?都不是。因此,它不是一个单独的WorkItem。但是这个用例告诉你有些东西是完全不同的;而且告诉你有些东西是需要离线支持的。因此,有些任务需要验证是否应该包含在前面提到的基础服务;例如连接检测,离线数据存储和消息队列刷新。

Sub-WorkItem或WorkItem

一部分用例在用例图中虽然像Sub-WorkItem,但是实际上是一级WorkItem。一般来说,这种情况经常发生在一个用例虽然逻辑上是有一个父用例的,但是仔细分析后发现它不跟其它子用例有任何关系,也不依赖共享状态,上下文或其他的共性。为了避免管理上的不必要,可以把这类用例作为一级WorkItem。仔细分析图5中的“Find Stock”和“Find Business”用例,这两个用例用于查询股票文章和订购股票交易。它们并不依赖于共享状态或者一级用例上下文。因此,这样的用例最适合作为一级WorkItem,可以通过注册到所在模块的命令来调用。

识别WorkItem的总结

总之,开发基于CAB的复杂智能客户端,基于用例图来识别WorkItem需要如下几个步骤:

1)       创建用例图;

2)       在CAB中将WorkItem和用例一一对应;

3)       考虑和客户端无关的特殊用例;

4)       分析用例之间的关系。假如用例被除了父用例之外的其他用例使用,那就有必要作为一级WorkItem;

5)       分析用例需求。假如用例相对来说比较孤立,它们也有必要作为一级WorkItem。

其中第4步和第5步是分析用例图的细化部分,用来针对用例之间特有的关系。最后,图8展示了从图5延伸出的类图。

业务对象驱动

针对小而简单的应用程序,比用例驱动更简单的方式就是业务对象驱动,它基于智能客户端处理的业务对象来区分和构造WorkItem。你可以创建一系列应用程序需要处理的业务对象,典型的业务对象如顾客,产品,股票,股票账户,股票债务,或者股票交易。每个对象都创建一个WorkItem。因为股票交易包括股票账户和股票债务,因此可以创建为WorkItem和Sub-WorkItem。

打包WorkItem到模块中

识别WorkItem后,需要把它们打包到模块中。一个模块就是一个部署单元。本质上,你要把逻辑上相关的,用来说明同一业务的WorkItems打包到一个模块中。以图8的用例图为例,你需要创建股票会商,股票业务和股票管理三个模块,如图9所示。

关于打包还需要考虑下面的因素:

²       安全性

²       配置

²       重用

首先,模块是通过配置文件进行配置的。默认情况下,这个配置文件就是应用程序根目录下的ProfileCatalog.xml。模块根据用户的角色进行配置。因此,决定如何打包的核心因素就是安全性。假定用户没有股票管理的权限,但是不允许创建股票和锁定股票的用户却需要查询股票来完成他的任务。当配置股票管理模块(根据图9,股票管理模块包含查询股票)时,只有后台的银行职员才可以使用这个模块,用户是没有权限的。但是用户也需要这个模块,所以有必要把查询股票打包到单独的模块。配置,重用的情况和安全性类似。假如其他的模块想要重用这个WorkItem,也有必要将它单独打包。假如你想单独配置这个WorkItem,也可以单独打包。在图9的例子中,正确的方法是单独打包“Find
Stock”,“Find Business”,“Stock Order Collection”,如图10所示。

假如你觉得一些可以单独封装的WorkItem在安全性、配置、重用的考虑上影响度差不多,如图10所示,你可以把它们统一打包到一个模块中,代替三个模块。例如对于所有的关键字查询的WorkItem都差不多,你可以专门创建一个单独的模块来封装。

CAB基本用法指南

用例之间的关系意味着用例之间的交互。大体上说,CAB支持两种交互方式—通过命令模式实现的主动交互和通过事件代理实现的被动交互。那么我该如何使用呢?

使用事件代理机制

CAB事件代理为处理松散耦合的事件预先创建了基础条件。装载到CAB的任何部件都可以发布事件,其他部件可以订阅这些事件且不需要知道发布者是谁。事件发布和订阅的关系是通过URIs事件建立的,URIs是事件的唯一标识符。

因此,使用事件代理来处理松散耦合方式的交互。假如CAB的部件(WorkItem,controller,presenter,SmartPart,service)想要提供信息给其他部件,同时也不需要立即得到回应,也不关心是谁需要这些信息,那么事件代理机制就是正确的选择。相反,如果部件需要立即得到回应或者需要知道是谁在和它通信,那么使用事件代理就是错误的。不要通过触发事件来实现立即的回应。尽量减少使用事件交互的组件间的关联,尽可能的减少是很有必要的。

使用命令模式

假如这个部件发出命令给接受者后,需要在接受者完成任务后继续做剩下的事情,或者需要将一个控件转交给另外的部件,可以通过命令模式来实现。启动WorkItem是通过命令模式而不是事件代理机制。使用命令模式用来触发具体的WorkItem执行相应功能。

抱歉!评论已关闭.