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的文档,应该有答案。
|