漫谈OCL概念、特征和实践

漫谈OCL概念、特征和实践

闲聊:

Jos WarmerObject Constraint Language, The: Getting Your Models Ready for MDA, Second Edition》终于看完了。这是一本不可多得的好书,一本好书可以让人节约很多的时间,其实我一直在寻找MDA相关规范的书,这本OCL是我认为最好的一本。关于MOF规范一直没有由浅入深的教程。XMI倒是有一本《精通XMI》,不过也不甚精彩。UML当然汗牛充栋,这倒不必多说了。

OCL概念

我的BLOG上面两篇都是介绍OCL的,导致人气低迷,本来关注MDA技术的人就不多,关注OCL的就更少了。不过无论如何,OCLMDA技术中不可缺少的部分。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转换为PSMclass的规则,以及为private属性添加gettersetter方法。

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类型的值UndefinedOCL的类型有三种:基本类型(IntegerBoolean等)、Collection类型(五种,虚类型Collection,以及它的子类型SetOrderedSetBagSequence)和自定义类型(UML类,AssociationEnumeration等等)。

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

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开发了一个KMFKent 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.1EMF2.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的文档,应该有答案。

后记

目前对OCL有了基本的把握,现在缺少的就是一个具体系统的构建实践。希望在EclipseEMFEclipseUMLKMF的环境下来完成这个例子。

你可能感兴趣的:(漫谈OCL概念、特征和实践)