1.
EMF的元模型Ecore 我們通常所說(shuō)的模型(Model)是指應(yīng)用程序更高層次的描述,通過(guò)它可以生成部分甚至全部的實(shí)現(xiàn)代碼,可以由UML等標(biāo)準(zhǔn)的方法來(lái)定義。EMF(Eclipse Modeling Framework)中的模型層次沒(méi)有這么高,它和實(shí)現(xiàn)直接關(guān)聯(lián)。
EMF是一個(gè)可以產(chǎn)生代碼的框架,你可以通過(guò)UML類圖、XML Schema、Java Interface等任何一種方式來(lái)定義EMF模型,而且可以由一種方式生成另外其它方式,在這里EMF 模型就是把這三者結(jié)合在一起的更高層次的一種表示。EMF模型本質(zhì)上是UML類圖的子集,它是關(guān)于應(yīng)用的類和數(shù)據(jù)的簡(jiǎn)單模型。
用來(lái)描述EMF模型的模型(元模型)叫做Ecore,Ecore本身也是一個(gè)EMF模型,因此它是自己的元模型,也可以說(shuō)Ecore是一個(gè)元元模型。Ecore模型的主要組成部分有:
EClass:代表一個(gè)類,通常有自己的名字,0個(gè)或者多個(gè)屬性、引用。
EAttribute:代表一個(gè)屬性,有自己的名字和類型。
EReference:代表兩個(gè)類之間的關(guān)聯(lián)關(guān)系的一端。有名字,代表是否聚合的boolean類型的標(biāo)志,以及目標(biāo)類的類型。
EDataType:代表屬性的類型,可以是int或者float等原子類型,也可以是java.util.Date等對(duì)象類型。
當(dāng)我們有了自己的具體應(yīng)用時(shí),我們會(huì)實(shí)例化Ecore的以上元素為這個(gè)具體的應(yīng)用構(gòu)造模型,稱為core模型。core模型的序列化采用XMI(XML Metadata Interchange),因?yàn)橛脕?lái)定義core模型的三種方式(Java Interface,XML Schema,UML)都有各自的缺點(diǎn),不適合作為core模型的序列化形式。
2.
目前生成Ecore模型的三種方法 (1)從UML生成: 直接用Ecore Editor或者Omondo提供的工具EclipseUML;也可以從EMF Project的Wizard中導(dǎo)入mdl文件;還可以從UML工具中導(dǎo)出。其中第一種方法可以自動(dòng)的同步core模型與工具中編輯的模型(如EclipseUML編輯的ecd文件),而后兩種方法當(dāng)uml類圖有了變化時(shí)需要重新導(dǎo)入或者導(dǎo)出才能使core模型與之同步。
(2)從Java Interface生成:在要建模的類或者方法前加“@model”注釋,每個(gè)屬性和引用對(duì)應(yīng)一個(gè)get方法,不需要set方法。
/**
*@model
*/
public interface PurchaseOrder
{
/**
@model
*/
String getShipTo();
/**
@model
*/
String getBillTo();
/**
@model type="Item" containment="true"
*/
List getItems();
}
(3)從XML Schema生成:模型中的每個(gè)類用一個(gè)complex type表示,一個(gè)屬性用一個(gè)內(nèi)嵌的element表示,引用則用一個(gè)內(nèi)嵌的屬于另一個(gè)complex type的element表示。這樣給定schema之后,模型實(shí)例的序列化根據(jù)它來(lái)進(jìn)行。
除了以上三種定義模型的方式之外,還存在其它的方式,比如說(shuō),目前EMF正在支持RDB Schema(Rational Database)的方式來(lái)定義模型。
3.
代碼生成 模型到代碼的自動(dòng)生成是EMF重要的功能之一,大大提高了生產(chǎn)率,而且生成的代碼簡(jiǎn)潔,高效。
Ecore中的每個(gè)類(EClass)會(huì)生成一個(gè)接口和一個(gè)實(shí)現(xiàn)類,EMF特意采用了這種實(shí)現(xiàn)與接口相分離的設(shè)計(jì)。其中的接口都會(huì)繼承EObject這一EMF中的集接口,它如同java.lang.Object在Java中的地位,它主要提供了三個(gè)功能:eClass()方法返回該對(duì)象的metaobject;eContainer()和eResource()反別返回包含該對(duì)象的對(duì)象和資源;另外eGet()、eSet()、eIsSet()和eUnset()等方法為訪問(wèn)該對(duì)象提供了API.EObject還繼承了Notifier接口,當(dāng)對(duì)象發(fā)生變化時(shí)(如成員變量的取值發(fā)生了變化)會(huì)發(fā)出通知(觀察者設(shè)計(jì)模式)。比如改變Order對(duì)象的屬性shipTo的set方法的實(shí)現(xiàn)如下所示:
public void setShipTo(String newShipTo)
{
String oldShipTo = shipTo;
shipTo = newShipTo;
if(eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, POPackage.PURCHASE_ORDER_SHIP_TO, oldShipTO, shipTo));
}
其中eNotificationRequired()是BasicNotifierImpl類提供的方法,它返回是否需要調(diào)用eNofifier().
除此以外,EMF還為每個(gè)模型分別生成一個(gè)factory和package對(duì)應(yīng)的接口和實(shí)現(xiàn)類。其中**Factory繼承于EFactory,為模型中每個(gè)類的創(chuàng)建提供create***方法,因此我們?cè)贓MF中一般不用new來(lái)創(chuàng)建對(duì)象。創(chuàng)建一個(gè)訂單對(duì)象的代碼如下:
PurchaseOrder po = POFactory.eINSTANCE.createPurchaseOrder();
如果**Factory中沒(méi)有為你想要的類型生成特定的create***()方法,那么可以用基類EFactory的create(EClass class)方法,將要?jiǎng)?chuàng)建的類型作為參數(shù)傳入即可。比如說(shuō),模型中用到Enumeration或者自定義的MapEntry,**Factory中就不會(huì)有直接創(chuàng)建這兩個(gè)類型的對(duì)象的方法,我們可以用***Factory.eINSTANCE.create(**Package.eINSTANCE.getIntToIntEntry)來(lái)完成。
**Package使得我們可以訪問(wèn)模型中的Ecore元數(shù)據(jù),模型中的每個(gè)類、每個(gè)屬性、每個(gè)引用都在**Package中對(duì)應(yīng)一個(gè)int值。
生成的**AdapterFactory類可以為特定的類型創(chuàng)建Adapter。
4.
代碼的重新生成與合并
EMF生成的類、接口、方法、域前面都有“@generated"標(biāo)記,如果改變?cè)摌?biāo)記,那么在重新生成代碼的時(shí)候,這個(gè)標(biāo)記下的部分保持不變,不會(huì)重新生成。如果你修改了getName()方法的實(shí)現(xiàn),也相應(yīng)的修改了該方法前面的"@generated"標(biāo)記,但是你既想保留自己對(duì)這個(gè)方法的修改,又想看看EMF自動(dòng)生成的這個(gè)方法是什么樣子的,這時(shí)可以通過(guò)在該類文件中添加這樣一個(gè)方法來(lái)實(shí)現(xiàn):
/**
*@generated
*/
public String getNameGen()
{
}
這個(gè)方法名字的末尾加上了Gen后綴,這樣EMF在生成代碼的時(shí)候發(fā)現(xiàn)你把getName()前面的“@generated"標(biāo)記修改了,這時(shí)它會(huì)檢查有沒(méi)有g(shù)etNameGen()方法存在,如果有,就把getName的默認(rèn)實(shí)現(xiàn)添加在這個(gè)方法里邊。重新生成代碼后,發(fā)現(xiàn)EMF為getNameGen()方法添加了實(shí)現(xiàn)語(yǔ)句"return name".
EMF代碼生成器是從generator model (.genmodel文件)而不是core model(.ecore文件)來(lái)生成代碼的,這里的generator model是core model的Decorator,其中的GenClass修飾EClass, GenFeature修飾EAttribute和EReference等,另外它還包含生成代碼的包名,生成的Package和Factory類的前綴名字等信息。
generator model和ecore model分離的好處是,Ecore元模型只保存模型信息,而獨(dú)立于代碼生成相關(guān)的一些額外信息。壞處是兩個(gè)模型可能出于不一致的狀態(tài),因此修改了ecore模型后,應(yīng)該在genmodel上重新導(dǎo)入ecore模型,以保持兩個(gè)模型的一致性。