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

Smart Client Software Factory

虽然说CAB为我们提供了强大的平台,同时学习CAB也绝对是一个挑战。进一步说,使用CAB需要开发者人工完成很多步骤,例如自己需要从WorkItem的基类派生Controller类,View类,Model类或者实现用例管理。SCSF是VS2005专业版的一个扩展工具,提供了很多功能来自动化使用CAB的过程,并且有丰富详尽的文档资料介绍如何使用,包括使用帮助和一套模拟出的具体项目实施指南,比如全球银行项目指南。

SCSF基于Guidance Automation Extension,这是由微软的pattern&practices team提供的工具。Guidance Automation Extension帮助架构师和开发人员为自动化一些典型开发任务提供扩展,以强化和确保项目中共用的一些指示和指南。SCSF为开发基于CAB的智能客户端程序和类似创建Model和用例管理器(除了事件发布和订阅)的自动化过程提供了向导。

WorkItemController:它是由SCSF定义的一个类,用来完成WorkItem中的初始化过程。当使用SCSF创建WorkItem时,我们并不是直接从WorkItem的基类继承,而是从WorkItemController继承来完成WorkItem的初始化。

ControllerWorkItem:这也是由SCSF定义的,基于WorkItemController初始化后的WorkItem类就是ControllerWorkItem。它调用WorkItemController进行初始化。

ModuleController:基于ModuleController的WorkItem是整个Module中所有WorkItem的根。SCSF默认创建了ModuleInit派生的Module类,它自动为模块创建了这个根。

Presenter:它只负责一个SmartPart和业务模型之间的逻辑,是基于MVP模式进行设计的,MVP是MVC模式简化后的一个变体。MVC和MVP的最大区别在于,MVP中View完全由Presenter来控制,而MVC中Controller和Model都可以控制View。

开发过程介绍

在前面提到过,开发基于CAB的智能客户端是以用例为中心的。因此,理解好基于客户端相关的业务非常关键。首先,影响开发过程有3个方面:Shell,infrastructure services和具体的用例。因此,设计复杂智能客户端要求依照如下三个准则:

1、首先需要深刻理解对大多数用例通用的需求。和UI相关的通用功能应该直接被整合到Shell中,或者至少考虑到Shell的布局和设计。和UI无关的通用功能应该作为核心服务。

2、创建具体的用例图。在设计中用例可以很好的转换为WorkItem或者sub-WorkItem。用例之间的交互可以通过命令模式或者CAB的事件代理模块来实现。逻辑上联系紧密的WorkItem,比如说它们实现的用例是针对同样的Actor,可以放在一个模块中。

3、细化用例图,分析用例之间的关系,重用度和安全机制。在细化过程中,需要慢慢的重构WorkItem。典型是找到一些本来属于其它模块的WorkItems,却被打包进这个模块,或者是与它们的父WorkItem无关,其实更适合放到first-level WorkItems中。First-level WorkItem只有一个上级WorkItem,及RootWorkItem。

需求分析的第一步由核心组来完成。在需求分析之后,核心组开始设计Shell和基础服务。其中非常关键的一点是必须找出针对Shell和基础服务最核心的需求,这些需求需要被转化为服务接口。

紧接着,第二组,应该是最熟悉业务的一组,开始进行具体用例分析。同时,核心组开始开发Shell和基础服务,Shell和基础服务的开发在开始应该是一次完成。完成时候,剩下的工作就是实现所有和客户端相关的业务需求的WorkItem。因此,Shell和基础服务应该是一次完成的。

因为它们几乎被每一个WorkItem使用,因此在更大范围内开发WorkItem之前必须尽可能的完成Shell和基础服务的开发。进一步说,在设计和开发大量WorkItem时,有必要先开发少量的WorkItem来验证整个架构是否可行。也许将来还会在Shell和基础服务中增加少量新的需求,这需要我们改变服务接口(根据你自身情况,在开始实际开发前先设计原型也行得通,这都由你自己决定了)。

在具体的用例分析中获得的用例是CAB开发中划分WorkItem的基础。使用用例驱动的方式设计WorkItem,每个用例映射为一个WorkItem。在用例图中展现的用例之间的关系提示我们哪些是sub-WorkItem,以及通过事件或者命令来处理它们之间的交互。典型的,研究和细化用例及之间关系可以识别出用例之间是何种关系(父子或者事件,命令)。这种研究细化会影响你如何将WorkItems组织到模块中。因此,时刻要求自己在开发更大范围用例前一定要先仔细分析。后面的章节会更详细的讲述。

总之,基于CAB来开发复杂智能客户端采用了一种层级方式,CAB提供了一个基本框架,Shell和基础服务库是我们应用程序的基础(也是建立于CAB上的一个框架),WorkItem中的Module负责具体的业务应用,可动态的加载到Shell中。因此我们可以从两种不同的方式来理解CAB:一种是CAB就是一种随时可以使用的工具,通过它可以嵌入我们的智能客户端程序;另外一种就是你可以基于CAB建立类似框架的结构,来构建适合你的业务的应用程序框架,下图展示了这种结构。


Shell和Infrastructure Service设计

在基于CAB的智能客户端程序中,要设计的第一个部分就是Shell。Shell是具体负责装载和初始化基本的客户端服务,装载和初始化模块,提供主应用程序UI和装载一级WorkItem的智能客户端的具体程序。主应用程序UI针对装载的每个模块都是平等的。本质上,Shell必须实现对所有用例共有的需求。典型的需要被增加到Shell中的共用界面元素如下:

菜单,工具栏和状态栏

导航控件,例如类似Outlook2003/2007风格的Outlook toolbar

一个通用的信息显示面板,在其中心位置为用户显示信息(例如错误和警告)

任务面板,在Office2003/2007中有,或者是任务栏,就是XP的任务栏

快捷提示面板,针对用户当前的操作给出提示

这些仅仅是告诉你如何较好的把共用界面元素整合到Shell中。通过Workspaces和UIExtensionSites,CAB提供了方便的机制帮助你在Shell中对界面进行扩展。Workspaces用来完全的替换整个UI界面,UIExtensionSites用来扩展现有UI界面的一部分,比如增加工具栏或菜单栏。如下图所示。


Workspace和UIExtensionSite对应用程序中所有的服务和加载的模块都是公开的。CAB允许你通过如下方式获取:

workItem.UIExtensionSites[“SiteName”].Add<ToolStripButton>(newToolStripButton());

虽然这种方式给你很大的自由度,但是你也许觉得开发人员通过“强类型“的方式与Shell交互。进一步说,这导致了Shell中各部分的紧耦合。因此,推荐在Shell和需要添加到Shell中的组件间创建一个夹心层。Shell需要提供一些功能给其它组件;你可以设计一个接口由shell(或者shell的主窗体对应的presenter)来实现,由此来扩展shell并把shell作为CAB其它组件共同使用的基础服务。如图4所示。


这是一个简单却重要的思想,因为装载到smart client的元素(WorkItem中或基础服务包含的Module)并不需要知道Workspace和UIExtensionSite的名称。这些元素可以通过如下方式获得服务:

IShellExtensionServiceShellService=workItem.Services.Get<IShellExtensionService>();

ShellService.AddNavigationExtension(“Somenew Menu”,null,someCommand);

通过实现IShellExtension可以解耦Shell的UI设计和基础服务。如下所示:

public class ShellLaoutViewPresenter:Presenter<ShellLayoutView>,

IShellExtensionService

    {

        public void AddNavigationExtension(...)

        {

            ToolStripButtonNewButton=new ToolStripButton();

 

           WorkItem.UIExtensionSites[UIExtensionSiteNames.OutlookNavBar]

.AddingNewEventArgs

                <ToolStripButton>(NewButton);

        }

 

        public void ShowInWorkspace(...)

        {

           WorkItem.Workspaces[WorkspaceNames.ContextWorkspace].

Show(smartPart,info);

        }

}

值得重视的是这个Shell接口暴露给其他组件使用仅仅是Shell中UI相关的功能。它也是装载到智能客户端的其它组件扩展菜单,工具栏,窗口左侧的任务栏,或者增加到通用信息面板中的消息(见前文)的切入点。

比较Workspace和UIExtensionSite

文章前面提到过,Workspace通过新UI完全替换Shell。典型的,这一步由应用程序模块中的WorkItem来实现。UIExtensionSite是Shell中可以不被完全替换的部分,而且它可以通过装载到智能客户端的模块进行扩展。典型的,它们可以用来启动一个WorkItem(用例);因此,大部分情况下它们通过ModuleInit类加载。

比较直接整合到Shell中和封装到分离的模块

当涉及到通用界面元素时,你经常需要选择是直接整合到Shell中还是封装到分离的基础模块中。关键因素就是重用性。你是否需要在其它地方使用这个通用UI?也许你需要在其它的客户端Shell中使用?或者其它Modul需要使用它显示,作为WorkItem界面的一部分?如果这样的话,你就应该进行封装而不是直接整合。

影响抉择的第二个因素是配置性和权限。假如你需要动态加载或者根据权限进行加载,那就要求将UI进行单独封装。

将Shell的布局单独封装或者直接放在Shell主应用程序中

当然,你可以将包含完整布局的Shell主窗体封装到单独的模块。事实上,当你使用SCSF创建新工程的时候会问你这个问题。但是你怎么选择呢?其实很简单:如果你想根据权限或者客户端的通用配置动态改变Shell的布局,那就必须进行单独封装。如果不是,那单独封装就是完全没有必要的。

把UI封装到用户控件中

你很想知道当你把UI直接放到Shell中或者将布局直接放到应用程序中而没有进行单独封装时究竟做了什么,因为需求总是不断变化的,而且你经常需要把这些工作外包到单独的模块。不管怎样,至少考虑到重构,你应该将UI中复杂的部分封装到单独的用户控件中,并且把逻辑部分封装到Presenter中。把这些模式牢记在心,以后的重构才不致付出更大的代价。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值