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