学习如何使用模式和IBM Rational XDE 2003来创建你自己的可复用框架。
显著节约成本的重用一直是软件工程的“圣杯”。从复制和粘贴源代码到类层次上的细粒度(fine-grained)复用,再到框架和体系结构层次上的粗粒度(coarse-grained)大规模复用,复用技术已经逐渐成熟。而在体系结构上的显著节约成本的重用应该成为所有开发项目都致力的一个战略性活动。在读过这篇文章后,你将会明白运用模式及IBM
Rational XDE V2003 如何创建你自己的可复用框架。
注释: 所有的模型和代码都使用XDE v2003 for .NET来描述。类似的的框架可以在J2EE环境下建立。代码语言采用C#,所以对于C++或JAVA程序员都应该是清晰易懂的。
大约二十多年前,软件工程业被比喻成旧时代的打铁。在那个时候,当你想建一座房屋,要去铁匠铺找所有需要使用的金属部件,从固定在墙上的烛台到锁门的铰链,甚至小小的钉子,都需要一个一个地手工来制造。这正如当时的软件工程--几乎所有的代码都是新的,每项规则和每个设计都是专门为正在构建的系统而编写,而且以后它们可能又需要重新被构建。代码复用(当时唯一可尝试的复用方式)可以被看成是在五金商店购买成百上千钉子,而不是自己亲手打造每一个钉子。这样,使用更为可靠的代码就可以缩短开发周期。
在20世纪80年代中期,经济可行的面向对象编程语言的引入,诸如C++(Smalltalk已出现,但并没有在业务环境中得到广泛使用),通过类直接支持代码的复用。复用方面已取得了一些重要的成果,但是预测中的“大跃进”并没有如期而至。这是由于开发者常常不能决定哪些类应当被复用。的确,一些代码的复用,使将来的代码编写更为快捷、可靠。但是,直到可视化建模和代码生成工具的出现,开发者才开始看到设计复用—概念及思想--带来了比代码复用更大的经济回报。他们意识到在体系结构及设计层次上的复用将带来相当巨大的收益。
现在打铁匠的类比法更具有说服力。随着设计模式的发明,而且从设计模型中产生代码非常简单,所以复用设计便成为一种理念。现在,一改过去复用“钉子”的作法,我们正在复用基础和墙壁,这类似于预先建造房屋的组成部分。我们不再是房子的建筑者,而是房子的设计者。不再需要请众多邻居帮忙钉木板建房,你现在可以直接买墙壁和屋顶,然后在几个小时之内就能够完成装配。
体系结构框架也是如此,为什么不从已有体系结构的20%,或者30%,甚至60%来开始呢?为什么不在应用中使用相同的构架,以便更易于软件工程师在工作中连续的应用?微软建立MFC类库,Sun提出Java和J2EE标准,他们都在作这方面的努力,而且,Microsoft目前又在尝试使用.NET来实现这种思想。正如他们90年代所作,更简单快捷、代价更低,并且更可靠地基于J2EE来构建Web应用。换而言之,像J2EE这样的框架为构建Web应用提供了经济显著的复用。
"优质、快速、低成本:只能同时选择其中两个"是一个旧的工程定律,其中的一个必须为另外两个而作出牺牲。但是复用体系结构框架是降低风险的一种途径,并且同时兼顾了这三个原则。
大规模的工业体系结构,比如J2EE,已积累了大量的经验与资源,它影响着项目、公司和工业之间的可复用性。在创建体系结构上投入大量的努力,仍然能带来显著的投资回报(ROI)。但是如果我们不使用J2EE,
.NET,或者一些其它的行业体系框架会怎样呢?或者要是我们的组织需要一个建立在J2EE或.NET之上的可复用体系结构,那又会怎样呢?我们可能只复用这个体系结构几次,或者几十次,而不是数千次,我们需要的是建立健壮的、可利用的架构能力。但是,快速却不够熟练的构建架构会给我们带来对于我们付出最小化的回报。
本文中,我们将使用设计模式快速建立框架,这是一个模式的模式。在XDE中应用这些模式将会产生大量代码。所以,实现这个框架的大多数代码不需要人工介入。即使这是使用模式所带来的唯一好处,我们仍然会有很多可用的代码。几行手写“粘合代码”将起到一个连接的作用。最后,我们将有一个3-tier
体系结构,它在可重复、可预测和健壮(基于组件)的框架中处理业务逻辑。这个框架可以复用(以单一模式应用,而不是重复敲入代码)来更快地建立其它应用架构。曾经从事这个框架的开发者在以后使用相同框架的应用时,不必再走弯路。框架的改进或加固会被快速地在应用间传播,这只需要通过重复应用模式即可,而不必多次手工修改代码。
我们将要建立的是一个简单的3-tier框架,它使用了三种模式处理业务逻辑:3-layer 体系结构模式、 Singleton模式和Command模式。Singleton和Command模式来源于Gang
of Four所编著的“设计模式”,使用XDE来实现。本文中使用到的Command模式为达到清楚明了,已作了一定的简化。
首先,我们来看看框架的行为。图1是一个时序图,描述框架将如何处理业务逻辑。
|
图 1: Business Logic Behavior |
业务对象以红色标注,用户接口对象是蓝色。当用户请求命令(应用功能)时,序列通过与用户接口对象相互作用来被执行。窗口是展示层的一部分,所以它不执行任何业务逻辑,但是它知道将请求传送到业务层。该窗口通过其业务逻辑层的接口来做这项工作,该接口是由BusinessLayerProxy类来实现的。这是一个控制器类,其职责是管理业务逻辑的执行。
BusinessLayerProxy建立或者访问CommandFactory实例,CommandFactory是一个Singleton,保证一个类在应用中仅有一个实例(对象)。使用Singleton模式,我们可以保证无论BusinessLayerProxy多少次尝试建立CommandFactory,也只存在一个实例。CommandFactory知道它是否已有一个实例,如果存在,则不建立另外的实例。
BusinessLayerProxy然后要求CommandFactory建立请求的业务逻辑对象(命令),这样,业务就可以被执行。这就是CommandFactory唯一的工作--知道如何建立适当的业务逻辑。在我们的示例中,整型ID被传送到CommandFactory,于是,建立正确的Command对象。本文中这个简单的策略很容易理解。在现实生活中,我们可能需要更健壮(和复杂)的方法来识别建立哪些业务逻辑。使用其它模式(比如Abstract
Factory 或Factory Method)能够减少维护开支,同时增加可用性。
所有的业务逻辑通过应用Command模式来封装在Command对象中。CommandFactory建立请求的业务逻辑的实例,并且将命令的接口传递给BusinessLayerProxy。BusinessLayerProxy并不清楚执行什么命令,它只是通过ICommand接口执行一些业务逻辑(注意CommandFactory的返回值)。一旦BusinessLayerProxy有了接口,便通过execute()操作来执行命令。这是Command模式的一个应用。
在这个例子中,使用了一些不错的OO设计,每个类应当只做一件事,并且把它做好。所以BusinessLayerProxy管理执行流,CommandFactory知道如何建立命令,每个command类封装--或是唯一的入口--用于单个应用服务的逻辑。只有经过业务接口,将表现层同业务层的变化隔离出来,业务层才是可访问的。封装、模块化和多态的应用均由模式来实现。XDE模式扩充的能力使得我们可以自由地应用这些好的设计。
我们从一个项目结构来开始(如图2所示)。这个结构是3-layer模式的应用,它把应用逻辑分成Presentation、Business和Integration/Persistence这几个类别。某个层的代码能且只能通过接口访问该层(如图3所示)。在本文中,我们主要关注业务层和显示层。在业务层,我们已有BusinessLayerProxy,它实现了IBusinessLayer接口并充当了整个业务层的管理者或控制器(如图4所示)。
|
图 2: Initial Project Structure |
所示3-layer模式就是它在被应用前的情形。Javascript被用做参数名的占位符。当应用这个模式时,Javascript将被实际的包/子系统名所代替,例如用“BusinessLayer”代替文本“<%=bizlayer%>”。
|
图 3: 3-layer Pattern |
应用的主窗体,“Form1”位于表现层,它必须通过其接口访问业务层(如图4)。在FORM1中有3个消息操作(..._Click()),都将使用关联“theBusinessLayer”来访问业务逻辑。
|
图 4: Main Form Accessing Business Logic |
代码与模型同步之后,我们能通过“theBusinessLayer” 访问业务逻辑。我门需要在消息处理中为FORM1写一行“粘合代码”,请求业务层执行适当的逻辑。ID“1”被传送到业务层,这样,就能够识别适当的业务逻辑。
private void Boltz_Click(object sender, System.EventArgs e)
{
theBusinessLayer.execute(1);
} |
既然我们已经访问了业务层,就需要建立CommandFactory类。我们通过应用Singleton模式,创造一个新类CommandFactory。Singleton模式如图5所示。
|
图 5: Singleton Pattern |
当我们应用这一模式时,“Singleton”被“CommandFactory”所取代。“Client”变成“BusinessLayerProxy”。GetUniqueInstance()是个静态方法,返回CommandFactory的实例。内部地,如果一个CommandFactory的实例不存在,则将会在创建一个。否则,将返回已存在的CommandFactory的实例。获取CommandFactory唯一的方法是通过GetUniqueInstance()操作。(注:Singleton的构造函数是私有的)。
现在BusinessLayerProxy已访问CommandFactory。我们需要通过命令模式识别业务逻辑的位置,当BusinessLayerProxy发出请求时,在CommandFactory中就会创建command,最终执行该逻辑。
图6显示的是命令模式的一个简化版本。所有的具体指令--所有的业务逻辑--实现命令接口。业务逻辑在execute()操作中实现。在现实的应用中,execute()可能利用类的协同来执行必要的业务逻辑。Invoker通过接口执行命令,而且Client创建命令,将参数传递给Invoker。对我们的目的来说,Command接口就是现有的ICommand接口(如图2所示)。Invoker总是BusinessLayerProxy,创建者就是CommandFactory。应用的模式如图7所示。
|
图 6: Simplified Command Pattern |
|
图 7: Applied Command Pattern |
每个命令(业务逻辑)通过使用Command模式来添加,每应用一个Command模式,便会创建一个新的ConcreteCommand(如:FirstCommand类),其它3个类总是复用的。这就提供了一种可预测的方法来添加,修改和调整业务逻辑。
我们写了一些“粘合代码”来连接CommandFactory和BusinessLayerProxy,这项工作只需做一次即可。我们也写了几行代码,以便从CommandFactory访问FirstCommand。本例中,我们使用的是简化的体系结构,所以每增加一个新的命令就需要操作一次。
我们在Form1中添加一些代码来访问业务层,以下便是BusinessLayerProxy中的代码,来请求和执行业务逻辑:
public void execute(int ID)
{
GetCommandFactory();
theCommandFactory.getCommand(ID).execute();
} |
GetComandFactory() 保证了CommandFactory被初始化。getCommand(ID)
引起 CommandFactory 创建command对象(请看下列代码), 而且 execute()
是ICommand的操作,执行业务逻辑。
以下是我们在CommandFactory中编写,来调用适当的业务逻辑:
ICommand theICommand;
public ICommand getCommand(int CommandID)
{
theICommand = null;
switch (CommandID)
{
1: theICommand = new FirstCommand();
break;
2: // put the next command here
break;
}
return theICommand;
} |
最后要做的是事情是:在FirstCommand的execute()操作中编写出业务逻辑。瞧!可不是!业务逻辑被合理地封装,业务层有一种简单且可复用的方法来处理业务逻辑,框架现在也已定义,可以用于创建其它的应用系统。再次应用整个框架时,速度将远远快于第一次。
模式的模式可以被创建,所以框架能被输送并复用于其它任何地方(如图8所示)。在XDE中,可以创建这个模式的模式并将它作为可复用资产规范(RAS)导出。其它项目可以将框架导入,并能够以单一的模式或者单独地应用其中一些模式来复用该框架。
|
图 8: Business Logic Framework (pattern of patterns)
|
“Undo”特性能被相对容易地添加到这个框架中,undo()操作可以被添加到ICommand,并且在每个ConcreteCommand中实现。不是在业务逻辑已经运行后删除命令对象,BusinessLayerProxy能把命令对象放在undo队列中,当用户调用undo,在行列顶端的Command对象会被实现,并且它的undo()
操作被调用。使用相同的业务逻辑将这种Undo逻辑封装起来,原始地传递数据。
日志能力可以很容易地增加到这个框架中。被称为“代码模板”的几段参数代码可以被添加,或者在XDE右击鼠标来实现。在模式中,当代码和模式同步时,把代码模板绑定到一个操作,便会生成相应代码。在Command模式中,将信息记录到文件中的代码能够被添加到ConcreteCommand的execute()操作。每个从command模式中建立的command类都会把信息写进日志,包括command类的名字或者模式中的任何参数信息。当代码模板已被绑定到execute()操作后,只击点击几次鼠标,再次应用command模式,便可以将功能添加到已存在的command类中。
框架甚至可以被创建,作为J2EE, .Net,或者其它的行业体系结构框架的抽象层。开发人员将创建使用这种框架的应用系统,以设计者和实施者都相当明晰的方式,依次建立适用于潜在体系结构的类。最后这个例子对于企业从多个供应商在体系结构上运行应用非常有用,但它必须被认真地对待。
体系结构和设计的复用在编码、维护、培训和易于理解这几个方面均带来了显著的功效。使用已在产品应用中经过验证的框架可以缩短开发时间,降低成本,并且提高产品质量。在本文中,我们经过运用3-layer,
Singleton, 和Command模式来处理业务逻辑创建框架,该框架可以在不同情况下复用。只需书写少量的代码来支持这个框架,使得它易于从一个应用程序移植到另外的一个中,并且从一开始就提高代码的质量。总之,可复用框架为开发组织提供了一个相当的竞争优势。
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文。
|