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