OCL的乐趣和威力 Part 1

会让我写这篇Blog的原因实在是因为OCL太好玩了,而且OCL的威力也逐渐让我有愈来愈深的体认,OCL现在已经成为我个人认为最重要的语言之一,我应该称OCL是『程式语言』?还是『正规语言』?还是『模型转换语言』?这我也不知道,这是因为OCL虽然是OMG的标准『正规语言』,大多数的人对于OCL的观念还停留在OCL是使用于UML模型中定义条件,限制,或是使用于MDA之中。这两者的应用都希望借由OCL能够更精确的定义模型的意义,以及借由OCL撰写和特定平台,语言,技术无关的企业逻辑,以便在实作或是应用MDA的模型转换过程中能够顺利产生有意义的结果模式,例如从PIM转换到PSM,从PSM转换到特定的程式语言,例如JavaC#或是Delphi等。

 

不过OCL在一些软体厂商,例如Borland,不断的改善之下,在原本只是唯读的OCL加入延伸的功能,以便让OCL具备修改和写入的能力。为什么要这样做? 请想一想,如果要在各种模型使用OCL做为和模型,平台,技术无关的中立语言,那么OCL只有唯读能力是不足的,例如在叙述图中使用OCL做为定义状态改变的标准语言,那么当状态改变时如何能够使用唯读的OCL来改变状态? 那要改变使用特定的程式语言来叙述吗?如果是这样的话,为什么还要使用OCL? 再想想,于模型转换时,我们需要有来源模型以及转换后的结果模型。结果模型是根据转换规则而产生的,因此如果我们决定使用OCL来定义,对映模型转换的规则,那么OCL必须具备建立结果模型的能力,否则我们要使用什么语言来叙述结果模型?

 

因此BorlandECO便根据OCL加以延伸形成Action Language,其中最重要的就是允许开发人员使用OCL进行修改,写入或是建立物件的功能。

x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBdEcWt85rBor7d_fdffz_68Vhdz0TAGLCwv0k2obTIk66cU0B8maWU32MRTstHKDVg0YwhOx0-9iT0HSUjwjZtAP3CHlGlZXU8ToibDN_RAhg

例如OCL借由加入:=运算元而拥有了指定功能之后,在状态图中我们才能够改变应用程式执行时的状态:

self.FirstName := home.owners->first.FirstName

 

至于Together 2006就对OCL加入了更多的能力了,因为Together 2006选择了使用OCL语言来定义模式和模式之间转换的规则。严格的说,应该是指Together 2006借由强化OCL语法让开发人员能够定义MetaModelMetaModel之间对映的关系,如此一来Together 2006就能够借由OCLMetaModel来转换不同的模型了。 这可以由我在上海/成都时展示的范例来说明。

 

200512月时,我在上海/成都展示了如何把一个由BPMN叙述的企业流程转换为UMLUseCaseBPMN模型通常是由商业人士使用来叙述企业领域的商业流程,当领域专家(Domain Expert)使用BPMN叙述了特定的企业流程之后,必须交由IT人员来开发系统。但是对于IT人员来说,BPMN模型可能很陌生,而且大多数的IT开发工具都只接受UML模型而不是更抽象的BPMN模型。因此我们需要把领域专家实作的商业模型转换为IT人员熟悉的UML模型。

 

当时我使用的BPMN模型如下所示:

 x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBemnQJgiKI1iGs4_v6lS3kVejFGiTeS6iD-ksaTGqdlCsKwGrEMEswqDFt4AQNmdMX_KPVjGiVyWer6EkmDi_8FRJ4tP8-qXaQbu7PTL3XYkw

我希望把这个BPMN的模型转换为UMLUseCase,如下所示:

x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBdNk_aVwsmKmgLR5Z6xBpoCAl93KOwQu9mk8kmKxuj9MVn8H9l2vf_PMB8FSTU1yyk24yQs0B31JgaT41XWtm-o09yqChoy-UoE5N-dzH-RLg

 

这要如何做到? 其实这正是MetaModelMetaModel之间转换的范例,要转换这两个特定的模型非常的简单,我们只需要定义BPMNMetaModel之间的每一个元素如何对映到UMLMetaModel的每一个元素,那么一旦这个对映规则定义好了之后,任何的BPMN就可以转换为UML之间的模型了,下图就说明了这个观念。

 x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBc2xIPJMJcBHyetUIRVouwgeNskiY0m-Yz_Za8sSuuj75Tw0IYFK0g3L7yQU2SJmmDttkwAsXH5O6e5ObVVTvscuCaiY9DveY2ZFyzRltvD6Q

 

001    transformation Bpmn_To_Uml;

002   

003    metamodel 'http://www.borland.com/together/2005/bpmn';

004    metamodel 'http://www.borland.com/together/uml';

005    metamodel 'http://www.borland.com/together/uml20';

006   

007    mapping main(in model: bpmn::BpmnProcessPool): uml::together::Model {

008        init {

009            var actors := model.lanes->select(lane | lane.oclIsKindOf(bpmn::BpmnLane))->oclAsType(Sequence(bpmn::BpmnLane));

010            var tasks := model.lanes.flowObjects->select(lane | lane.oclIsKindOf(bpmn::BpmnTask))->oclAsType(Sequence(bpmn::BpmnTask));

011        }

012        object {

013            ownedMembers := actors->collect(a | makeActor(a))->asOrderedSet();

014            ownedMembers += tasks->collect(t | makeUseCase(t))->asOrderedSet();

015        }

016    }

017   

018   

019    mapping makeActor(in lane: bpmn::BpmnLane): uml20::usecases::Actor {

020        object {

021            name := lane.name;

022            description := lane.documentation;

023           

024        }

025    }

026   

027    mapping makeUseCase(in task: bpmn::BpmnTask): uml20::usecases::UseCase {

028        object {

029            name := task.name;

030            description := task.documentation;

031        }

032    }

 

007行的mapping main定义了一个QVT转换的主进入点,这是一个什么样的转换呢?main signature就可以了解,它接受一个BPMN的流程模型,

(in model: bpmn::BpmnProcessPool)

转换并且回传一个Together的模型

uml::together::Model

 

接著我们定义了下面两个对映MetaModelMetaModel之间元素的规则:

n          每一个BPMN MetaModel之中的Lane元素都对映到UMLUseCase图形中的Actor元素

n          每一个BPMN MetaModel之中的Task元素都对映到UMLUseCase图形中的UseCase元素

 

有了这两个规则之后,一切就简单了。首先我们从来源BPMN模型中找到所有Lane元素以及Task元素,接著把Lane元素转换Actor元素,把Task元素转换为UseCase元素,工作就完成了。让就让我们解释一下前面的OCL如何完成它的工作。

一个转换主进入点可以分为两个部份,第一个部份称为init区块,它的目的主要是宣告变数或是查询转换过程需要使用的模型元素。因此在009行中宣告了一个actors变数,它的数值就是来源BPMN模型中所有的Lane元素。为什么,让我们看看它使用的OCL代表的意义。009行使用了如下的OCL程式码:

model.lanes->select(lane | lane.oclIsKindOf(bpmn::BpmnLane))->oclAsType(Sequence(bpmn::BpmnLane)

 

让我们猜拆解它的语法和009行意如下:

程式码段

意义

model.lanes

取得来源BPMN模型中所有的Lane物件

->select

由于model.lanescollection 物件,因此使用->代表符号的左边是collection 物件。Select 类似SQLSelect语法

(lane | lane.oclIsKindOf(bpmn::BpmnLane))

宣告一个暂时变数lane,这个lane如果是bpmn中的BpmnLane物件,也就是我们需要的Lane物件,它的代表符号是bpmn::BpmnLane。那么就符合select条件而进入结果物件资料集中(object resultset)

->oclAsType(Sequence(bpmn::BpmnLane)

 

由于->select(lane | lane.oclIsKindOf(bpmn::BpmnLane)) collection 物件,因此使用->代表符号的左边是collection 物件。

oclAsTypeOCL的函式,这个函式把(bpmn::BpmnLane转换为OCLSequence资料型态

 

了解了009行的意义之后,您应该可以了解010行的意义。

 

在主进入点的init区块之后就是主进入点的主要部份了,也就是主进入点回传的结果物件。这第二个区块是由object {}包围的。在object {}区块中,object就代表回传的uml::together::Model模型,那么回传的模型中包含什么东西呢?这就是由在object {}区块中的OCL程式码来决定的,也就是013014行的执行结果。看看013行在做什么?

013            ownedMembers := actors->collect(a | makeActor(a))->asOrderedSet();

 

程式码段

意义

ownedMembers

结果模型之中的特性值,代表结果模型中拥有的元素

:=

指定符号,把:=符号右边的执行结果指定给:=符号左边的特性值

actors->

009行宣告的变数,它是collection 物件,因此使用->代表符号的左边是collection 物件

collect(a | makeActor(a))

 

collectOCL的函式,它把()中的执行结果形成一个collection 物件。而a是暂时变数,这个变数呼叫makeActor对映函式

->asOrderedSet();

把这行程式码的执行结果转换为一个有次序的结果collection 物件。

 

019行的makeActor是一个对映函式,它接受一个bpmn::BpmnLaneLane物件并且回传一个UML 20UseCase模型中的  Actor物件。

 

这整个转换流程可以使用下面的图形来说明:

 x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBc5F9SrsXFHiA_H9j9oSZq8od3gdWZXUglH9TXdXnHu0FmoKFwfzDAWi8f1PgpylWyzsdtk9i5XbA-bj03qrCbuDdde-xEpxj9VzT6KiHdDHg

由这个范例可以看到OCL语言强大的力量,Together 2006提供的OCL支援包含了执行OCL转换能力,除错OCL转换程式,更提供了MetaModel类似Code Insight的功能,可以帮助开发人员在撰写OCL转换程式时对于模型和语法的帮助。例如下图就是在Together 2006OCL编辑器中打入model.之后,Code Insight视窗便可以出现指引开发人员在之后能够撰写的程式码。

 x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBc5G9QWCXnLphd7ls3rm-Tb4INru9FsYZkVAqVPt7sUXqKm61oEi2eYMX-oFtOGtE8lUa-nek3lGkaPgUsx3A32Kf8ZJniZeC0Rrg8u1KFblA

除了BPMN模型之外,Together 2006也提供了其他广泛模型转换的能力,例如我们也可以对EclispeEMF模型进行各种不同的转换,再结合EMF产生Java程式码的能力最终把PIM转换为JavaPSM

 x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBda2EUEvutMHgsNgTETie1zhc2TCP3jRvthcF8L3gXQJTK8wa9CqwsWbPZzM8GpaWPkGXS6NgS1y86DGAYzKu8wI8KzMb0uP8KdRfF1xb433Q


x1phiCZJPhsUYg-Rf0H_plhOdt-APWUfu3CSGTnBbEv4pwpNZa2x7IfMpOc2ikrRoQPOyRLoG4E8H8mOXvs7IgegZxrx7auzsUQ76ZDqGG6zV4x1phiCZJPhsUYg-Rf0H_plhOc4eM0crqUhZ7uNjI_RjYLk8imP4ETz41-2JI-bYbDaIg1IJjFmf9qEQ8O2vodrHotLAmKsnWQj8bp0kTQZ2YCY
x1phiCZJPhsUYg-Rf0H_plhORHfbvm1n8U-0ac3kHUwoTS2kEG25zSyaAgRhgwqnX6B9SD7Fr42Wwy6K1Pd-lihKJ0wRv22vhl78p3IkJQlcKUx1phiCZJPhsUYg-Rf0H_plhOQqyV-_97EJ56MixNyMXJWmcvib_XY7_s53KOpWWrrRTyYPdDotlXeF1aa4QGC7jLOg6npr_u1w3MavajQwW_gk
x1phiCZJPhsUYg-Rf0H_plhORiacwrHuYDXh2_YmoSpMzOmmKG-WjoDELZGanlltj_9tn0sm4KEGtunGEuAMSTfu6u55O6wm1ngnZkyk7Rtvy0x1phiCZJPhsUYg-Rf0H_plhOQozoVmKMXr9GBvt-CUeSo6Ro2zMFZ1amqmlKTUvG_aivbAfdD13SV6famuy8Iq1pT9TR0T4UcqRW5MlVRC1HpI
x1phiCZJPhsUYg-Rf0H_plhOXpgoPvXuGmqcBrUdJS1ejySbbFNDTDlYs5kLhqKbCilG-mRDhVU68rrHFq4nTTXTTp8cNmRzUNR9OQh0TP6fskx1phiCZJPhsUYg-Rf0H_plhOcduZ-4ZRT3oIpe0kOburZT73gVg1JHvWPl_GR2VPz9k0Vupnu9417V2xdOq7qnZELxbWx3lfPBqls_VXJpJyIE
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章