漫談OCL概念、特征和實(shí)踐
閑聊:
Jos Warmer的Object Constraint Language, The: Getting Your Models Ready for MDA, Second Edition》終于看完了。這是一本不可多得的好書,一本好書可以讓人節(jié)約很多的時(shí)間,其實(shí)我一直在尋找MDA相關(guān)規(guī)范的書,這本OCL是我認(rèn)為最好的一本。關(guān)于MOF規(guī)范一直沒(méi)有由淺入深的教程。XMI倒是有一本《精通XMI》,不過(guò)也不甚精彩。UML當(dāng)然汗牛充棟,這倒不必多說(shuō)了。
OCL概念
我的BLOG上面兩篇都是介紹OCL的,導(dǎo)致人氣低迷,本來(lái)關(guān)注MDA技術(shù)的人就不多,關(guān)注OCL的就更少了。不過(guò)無(wú)論如何,OCL是MDA技術(shù)中不可缺少的部分。OCL雖然號(hào)稱“對(duì)象約束語(yǔ)言”,不過(guò)實(shí)際上可以用來(lái)約束MOF四層模型中任意一層的模型以及實(shí)例。它真正的意義是建模相關(guān)領(lǐng)域約束語(yǔ)言。
除了約束模型以外,OCL的一個(gè)重要用途是可以用來(lái)描述模型轉(zhuǎn)換規(guī)則。雖然這并不是OCL的主要用途(我沒(méi)有仔細(xì)查閱OCL規(guī)范,不知道是否在規(guī)范中正式提出過(guò)這個(gè)用途),但是很多研究者進(jìn)行了研究和探索。其中Jos Warmer專門在一節(jié)中討論了這個(gè)問(wèn)題。下面是摘自他文章中的一個(gè)用OCL描述模型轉(zhuǎn)換的例子:
1. 自然語(yǔ)言描述的轉(zhuǎn)換規(guī)則
· 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)
從上面可以知道,這是一個(gè)將PIM中的class轉(zhuǎn)換為PSM中class的規(guī)則,以及為private屬性添加getter和setter方法。
2. 用OCL寫的轉(zhuǎn)換規(guī)則,其中用到了作者自己發(fā)明的偽符號(hào)
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
看來(lái)作者很有興趣擴(kuò)展OCL,將它變?yōu)?/SPAN>Model Transformation Language。
另外,Kent大學(xué)的研究者D.H.Akehurst在《Relations in OCL》(http://www.cs.kent.ac.uk/pubs/2004/2007/index.html)一文中專門做出了探索。這篇文章的大意是:擴(kuò)展目前的OCL,將Relation(關(guān)系)作為first class(第一性的)元素加入OCL語(yǔ)言,然后利用Relation和目前的OCL相結(jié)合來(lái)描述模型轉(zhuǎn)換規(guī)則(這篇文章我也許會(huì)在以后專門討論)。中文的例子比較長(zhǎng),相關(guān)知識(shí)較多,就不列舉了。
OCL特征
關(guān)于OCL的特征書中做出了總結(jié),這里我沒(méi)有回頭仔細(xì)找,而是就腦海中最深的一些映像說(shuō)幾點(diǎn)。
OCL是一個(gè)查詢性的語(yǔ)言,也就是說(shuō)任何OCL的動(dòng)作都不會(huì)對(duì)模型本身造成任何的影響或者改變。例如select操作,選出原來(lái)Set的一個(gè)子集,collect操作,將原來(lái)集合中的一些元素的值組成另一個(gè)集合。這些操作都不會(huì)對(duì)模型本身造成影響,最多就是構(gòu)建了另外的對(duì)象或者集合。
OCL是一個(gè)強(qiáng)類型的語(yǔ)言,任何一個(gè)元素,都是有類型的,并且任何操作的返回值一定有一個(gè)確定的類型。如果不能確定類型,那么此元素屬于OclVoid類型的值Undefined。OCL的類型有三種:基本類型(Integer,Boolean等)、Collection類型(五種,虛類型Collection,以及它的子類型Set,OrderedSet,Bag,Sequence)和自定義類型(UML類,Association,Enumeration等等)。
OCL里面很強(qiáng)調(diào)時(shí)間點(diǎn),任何操作都定義為瞬時(shí)完成的,即操作中模型的狀態(tài)不會(huì)改變。基于實(shí)現(xiàn)考慮,這么規(guī)定有一定的道理,不然在多線程系統(tǒng)中,OCL約束很有可能失效。另外precondition和postcondition也明確規(guī)定是在方法執(zhí)行的前后時(shí)間點(diǎn)才有約束,時(shí)間點(diǎn)不對(duì)約束無(wú)效。
OCL是一個(gè)宣言式(Declarative)的語(yǔ)言,描述了what to do,沒(méi)有描述how to do。例如self.attribute->select(i| i.name = ‘wxb_nudt’)描述了將某個(gè)類的所有attribute組成一個(gè)集合,然后將屬性名為wxb_nudt的屬性提取出來(lái)組成一個(gè)子集(顯然這樣的屬性不會(huì)多于一個(gè),但這并不是我們關(guān)心的問(wèn)題)。這個(gè)表達(dá)式描述以上的目的,但是沒(méi)有給出執(zhí)行過(guò)程。
OCL是基于集合論和謂詞邏輯的,這點(diǎn)從它的表達(dá)式中可以很輕易的看出來(lái)。但是并不是集合論中所有的集合操作在OCL中都具有相應(yīng)的符號(hào)表達(dá)。例如映射(project)就沒(méi)有。而且OCL沒(méi)有證明集合論中的所有集合操作都可以用OCL中現(xiàn)有的操作組合出來(lái)。但是我們相信這一點(diǎn)(盲目的,我沒(méi)有時(shí)間去證明這個(gè),呵呵)。另外關(guān)于OCL操作的中止性沒(méi)有得到證明,也就是說(shuō)“不能確定每個(gè)OCL操作都可以在有限時(shí)間內(nèi)完成”,并且OCL并不能保證任意的OCL表達(dá)式是可中止的(我感覺(jué)自己簡(jiǎn)直就在說(shuō)廢話)。其實(shí)OCL已經(jīng)說(shuō)明了,無(wú)論如何實(shí)現(xiàn),OCL假定所有表達(dá)式的計(jì)算都在瞬間完成。
雖然這部分內(nèi)容在前面的blog中提到過(guò),不過(guò)那個(gè)時(shí)候僅僅是照本宣科,和現(xiàn)在心有所感是不一樣的。
OCL實(shí)踐
目前OCL沒(méi)有標(biāo)準(zhǔn)的實(shí)現(xiàn),Jos Warmer在他的個(gè)人網(wǎng)站上列出了目前可用的OCL實(shí)現(xiàn)列表http://www.klasse.nl/ocl/ocl-services.html。
其中我選擇了Kent大學(xué)的OCL實(shí)現(xiàn)。還是kent大學(xué)的D.H.Akehurst,他們的research team開(kāi)發(fā)了一個(gè)KMF(Kent Model Framework),其中有一個(gè)OCL的實(shí)現(xiàn)。可以用來(lái)體驗(yàn)一下用OCL來(lái)編程(編程?不是建模么?)。下載地址http://www.cs.kent.ac.uk/projects/ocl/
需要給他們寫email才能得到下載地址。
然后Zurich大學(xué)的一位研究人員寫了這個(gè)版本的OCL的簡(jiǎn)單實(shí)踐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包環(huán)境下運(yùn)行了這個(gè)例子,結(jié)果如下:
[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.
例子很簡(jiǎn)單,詳細(xì)的解釋在上面鏈接的文章中解釋了。但是這個(gè)例子僅僅構(gòu)造了一個(gè)簡(jiǎn)單的ECore模型,而且是在程序中構(gòu)造的,不是使用EclipseUML或者EMF畫出來(lái)的,另外如何將OCL和模型連接起來(lái)也沒(méi)有提到。如果有時(shí)間,我會(huì)看看KMF的文檔,應(yīng)該有答案。
后記
目前對(duì)OCL有了基本的把握,現(xiàn)在缺少的就是一個(gè)具體系統(tǒng)的構(gòu)建實(shí)踐。希望在Eclipse、EMF、EclipseUML和KMF的環(huán)境下來(lái)完成這個(gè)例子。