Monday, September 14, 2009

漫谈OCL概念、特征和实践

更多精彩请到 http://www.139ya.com

漫谈OCL概念、特征和实践

作者:大雁北飞 出处:csdn

OCL概念

我的BLOG上面两篇都是介绍OCL的,导致人气低迷,本来关注MDA技术的人就不多,关注OCL的就更少了。不过无论如何,OCL是MDA技术中不可缺少的部分。OCL虽然号称“对象约束语言”,不过实际上可以用来约束MOF四层模型中任意一层的模型以及实例。它真正的意义是建模相关领域约束语言。

除了约束模型以外,OCL的一个重要用途是可以用来描述模型转换规则。虽然这并不是OCL的主要用途(我没有仔细查阅OCL规范,不知道是否在规范中正式提出过这个用途),但是很多研究者进行了研究和探索。其中Jos Warmer专门在一节中讨论了这个问题。下面是摘自他文章中的一个用OCL描述模型转换的例子:

1. 自然语言描述的转换规则

· For each class named className in the PIM, there is a class named className in the PSM.

· For each public attribute named attributeName : Type of class className in the PIM the following attributes and operations are part of the class className in the target model.

- A private attribute with the same name: attributeName : Type

- A public operation named with the attribute name, preceded with 'get' and the attribute type as return type: getAttributeName() : Type

- A public operation named with the attribute name, preceded with 'set' and with the attribute as parameter and no return value: setAttributeName(att : Type)

从上面可以知道,这是一个将PIM中的class转换为PSM中class的规则,以及为private属性添加getter和setter方法。

2. 用OCL写的转换规则,其中用到了作者自己发明的伪符号


Transformation ClassToClass (UML, UML) {

source c1: UML::Class;

target c2: UML::Class;

source condition -- none

target condition -- none

mapping

try PublicToPrivateAttribute on

c1.features <~> c2.features;

-- everything else remains the same

}

Transformation PublicToPrivateAttribute (UML, UML) {

source sourceAttribute : UML::Attribute;

target targetAttribute : UML::Attribute;

getter : UML::Operation;

setter : UML::Operation;

source condition

sourceAttribute.visibility = VisibilityKind::public;

target condition

targetAttribute.visibility = VisibilityKind::private

and -- define the set operation

setter.name = 'set'.concat(targetAttribute.name)

and

setter.parameters->exists( p |

p.name = 'new'.concat(targetAttribute.name)

and

p.type = targetAttribute.type )

and

setter.type = OclVoid

and -- define the get operation

getter.name = 'get'.concat(targetAttribute.name)

and

getter.parameters->isEmpty()

and

getter.returntype = targetAttribute.type;

mapping

try StringToString on

sourceAttribute.name <~> targetAttribute.name;

try ClassifierToClassifier on

sourceAttribute.type <~> targetAttribute.type;

}


-- somewhere the rules StringToString and ClassifierToClassifier

-- need to be defined

看来作者很有兴趣扩展OCL,将它变为Model Transformation Language。

另外,Kent大学的研究者D.H.Akehurst在《Relations in OCL》(http://www.cs.kent.ac.uk/pubs/2004/2007/index.html)一文中专门做出了探索。这篇文章的大意是:扩展目前的OCL,将Relation(关系)作为first class(第一性的)元素加入OCL语言,然后利用Relation和目前的OCL相结合来描述模型转换规则(这篇文章我也许会在以后专门讨论)。中文的例子比较长,相关知识较多,就不列举了。
OCL特征

关于OCL的特征书中做出了总结,这里我没有回头仔细找,而是就脑海中最深的一些映像说几点。

OCL是一个查询性的语言,也就是说任何OCL的动作都不会对模型本身造成任何的影响或者改变。例如select操作,选出原来Set的一个子集,collect操作,将原来集合中的一些元素的值组成另一个集合。这些操作都不会对模型本身造成影响,最多就是构建了另外的对象或者集合。

OCL是一个强类型的语言,任何一个元素,都是有类型的,并且任何操作的返回值一定有一个确定的类型。如果不能确定类型,那么此元素属于OclVoid类型的值Undefined。OCL的类型有三种:基本类型(Integer,Boolean等)、Collection类型(五种,虚类型Collection,以及它的子类型Set,OrderedSet,Bag,Sequence)和自定义类型(UML类,Association,Enumeration等等)。

OCL里面很强调时间点,任何操作都定义为瞬时完成的,即操作中模型的状态不会改变。基于实现考虑,这么规定有一定的道理,不然在多线程系统中,OCL约束很有可能失效。另外precondition和postcondition也明确规定是在方法执行的前后时间点才有约束,时间点不对约束无效。

OCL是一个宣言式(Declarative)的语言,描述了what to do,没有描述how to do。例如self.attribute->select(i| i.name = ‘wxb_nudt’)描述了将某个类的所有attribute组成一个集合,然后将属性名为wxb_nudt的属性提取出来组成一个子集(显然这样的属性不会多于一个,但这并不是我们关心的问题)。这个表达式描述以上的目的,但是没有给出执行过程。

OCL是基于集合论和谓词逻辑的,这点从它的表达式中可以很轻易的看出来。但是并不是集合论中所有的集合操作在OCL中都具有相应的符号表达。例如映射(project)就没有。而且OCL没有证明集合论中的所有集合操作都可以用OCL中现有的操作组合出来。但是我们相信这一点(盲目的,我没有时间去证明这个,呵呵)。另外关于OCL操作的中止性没有得到证明,也就是说“不能确定每个OCL操作都可以在有限时间内完成”,并且OCL并不能保证任意的OCL表达式是可中止的(我感觉自己简直就在说废话)。其实OCL已经说明了,无论如何实现,OCL假定所有表达式的计算都在瞬间完成。

虽然这部分内容在前面的blog中提到过,不过那个时候仅仅是照本宣科,和现在心有所感是不一样的。
OCL实践

目前OCL没有标准的实现,Jos Warmer在他的个人网站上列出了目前可用的OCL实现列表http://www.klasse.nl/ocl/ocl-services.html。

其中我选择了Kent大学的OCL实现。还是kent大学的D.H.Akehurst,他们的research team开发了一个KMF(Kent Model Framework),其中有一个OCL的实现。可以用来体验一下用OCL来编程(编程?不是建模么?)。下载地址http://www.cs.kent.ac.uk/projects/ocl/

需要给他们写email才能得到下载地址。

然后Zurich大学的一位研究人员写了这个版本的OCL的简单实践http://www.zurich.ibm.com/~wah/doc/emf-ocl/,源代码如下:


import java.util.List;



import org.eclipse.emf.ecore.EAttribute;

import org.eclipse.emf.ecore.EClass;

import org.eclipse.emf.ecore.EcoreFactory;

import org.eclipse.emf.ecore.EcorePackage;



import uk.ac.kent.cs.kmf.util.ILog;

import uk.ac.kent.cs.kmf.util.OutputStreamLog;

import uk.ac.kent.cs.ocl20.OclProcessor;

import uk.ac.kent.cs.ocl20.bridge4emf.EmfOclProcessorImpl;



public class OCLDemo {



public static boolean checkOCLConstraint(OclProcessor processor, String expr, Object model) {

List l = processor.evaluate(expr, model);

return Boolean.valueOf(l.get(0).toString()).booleanValue();

}



public static void main(String[] args) {

ILog log = new OutputStreamLog(System.err);

OclProcessor processor = new EmfOclProcessorImpl(log);

System.out.println(processor.evaluate("1+1"));



processor.addModel(EcorePackage.eINSTANCE);

EClass eClass = EcoreFactory.eINSTANCE.createEClass();

eClass.setName("Library");

EAttribute attr = EcoreFactory.eINSTANCE.createEAttribute();

attr.setName("books");

attr.setEType(EcorePackage.eINSTANCE.getEInt());

eClass.getEStructuralFeatures().add(attr);



System.out.println(processor.evaluate("context ecore::EClass " +

"inv:self.eAttributes->select(x|x.name='books')", eClass));



System.out.println(processor.evaluate("context ecore::EClass " +

"inv:self.eAttributes->exists(x|x.name='books' and x.eType.name = 'EInt')", eClass));



boolean pre = checkOCLConstraint(processor, "context ecore::EClass inv: not self.oclIsUndefined()",eClass);

// do something with eClass

boolean post = checkOCLConstraint(processor, "context ecore::EClass inv: self.eAttributes->forAll(c| not c.changeable)",eClass);

if (!(!pre | post))

System.out.println("OK.");

else

System.out.println("Ooops.");



}

}


我在Eclipse3.0.1和EMF2.0以及上面下载的OCLjava包环境下运行了这个例子,结果如下:

[2]

[[org.eclipse.emf.ecore.impl.EAttributeImpl@62937c (name: books) (ordered: true, unique: true, lowerBound: 0, upperBound: 1) (changeable: true, volatile: false, transient: false, defaultValueLiteral: null, unsettable: false, derived: false) (iD: false)]]

[true]

OK.

例子很简单,详细的解释在上面链接的文章中解释了。但是这个例子仅仅构造了一个简单的ECore模型,而且是在程序中构造的,不是使用EclipseUML或者EMF画出来的,另外如何将OCL和模型连接起来也没有提到。如果有时间,我会看看KMF的文档,应该有答案。

No comments: