爲了理解EMF到底是什麼,你只須要知道一件事:「模型」(model)是什麼?「模型」的目的是什麼?html
EMF不要求全新的方法論亦或是任何複雜的建模工具。只須要從Eclipse的Java開發工具着手開始。java
EMF將建模概念直接與其實現相關聯,因此上手比較容易。程序員
舉個編程實例,假設老闆讓你編寫一個程序來管理供應商的採購清單。採購清單包含付款對象(bill to)和送貨對象(ship to)的地址,以及貨物的集合。其中,貨物信息包含名稱、數量、價格。編程
//採購清單 public intrerface PurchaseOrder { //送貨對象 String getShipTo(); void setShipTo(String value); //付款對象 String getBillTo(); void SetBillTo(String value); //貨物的集合 List getItems(); }
//貨物信息 public intrerface Item { //貨物名稱 String getProductName(); void setProductName(String value); //貨物數量 int getQuantity(); void setQuantity(int value); //貨物價格 float getPrice(); void setPrice(float value); }
從上面的接口着手,你將須要開始編寫應用程序的UI以及以後一系列工做。框架
然而,這時候你的老闆問你:「你不先創建模型嗎?」儘管你以爲有點畫蛇添足,但仍是按吩咐創建了UML模型。
接下來須要保存這些「模型」,而後你決定使用XML文件來解決,因而寫下了XML Schema來定義你XML文件的結構:eclipse
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:po="http://www.example.com/SimplePO" targetNamespace="http://www.example.com/SimplePO"> <xsd:complexType name="PurchaseOrder"> <xsd:sequence> <xsd:element name="shipTo" type="xsd:string"/> <xsd:element name="billTo" type="xsd:string"/> <xsd:element name="items" type="po:Item" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name-"Item"> <xsd:sequence> <xsd:element name="productName" type="xsd:string"/> <xsd:element name="quantity" type="xsd:int"/> <xsd:element name="price" type="xsd:float"/> </xsd:sequence> </xsd:complexType> </xsd:schema>
在進行下一步工做前,你意識到,針對同一事物:程序的「數據模型」,你已經擁有三種不一樣的表示。因而你在思考:能不能只編寫三者中的一種模型,其餘兩種從這一種模型中生成?更進一步,在這種模型中是否有足夠的信息來生成接口的Java實現。
因而,EMF就出現了。EMF是一個框架和代碼生成工具,藉助EMF這個橋樑,你能夠用任意一種表示來定義一個模型,而後從中生成其餘表示以及相應的實現類。下圖展現了EMF是如何統一Java、XML、UML的。編輯器
有個常常問到的問題:「我是該建模呢仍是該編程?」,它的答案是:「均可以,建模亦或編程,這並非問題。」在EMF看來。建模和編程能夠被認爲是等價的。
對於初學者,相比於代碼,建模更容易使他們描述出應用程序的功能。若是你是個經驗豐富的程序員,若是對高階建模的想法沒有很大的信心,可將EMF看做建模的文雅介紹以及蘊含的優點。你沒必要接觸一種全新的方法論,但你也能享受的建模的一些好處。
若是你已經瞭解了建模的知識,甚至是MDA的重點,你應該講EMF看做在那個方向的一種技術。問題是高階建模語言還須要去學習,此外,由於咱們將須要處理(例如調試)生成的Java代碼,因此還須要理解它們之間的映射。優秀的傳統Java編程是作這件事最簡單和最直接的方式。
咱們認爲,EMF將建模與編程完美地相結合,以最大限度地發揮二者的效果。
在EMF看來,用戶和其餘開發者沒必要去了解高階建模語言和生成的Java代碼之間的映射,這些映射讓Java程序員來理解是天然而又簡單的。同時,應用程序之間的細粒度數據集成;代碼生成產生的生產力增益,這些是建模的優點。工具
用於表示EMF中模型的模型稱爲Ecore。Ecore自己就是EMF模型,所以是它本身的元模型。也能夠說Ecore是個元元模型。
元模型是模型的模型,若是該模型自己是一個元模型,那麼這個元模型實際上就是元元模型。
元模型的概念也能遞歸到元元元模型(meta-meta-metamodel)等等,可是咱們目前用不到。
下圖給出了一個簡化的Ecore元模型,說它是簡化的,是由於它只是Ecore元模型的子集,並且爲了方便,將某些公共類省略,如ENamedElement類(定義了類中屬性的名字)。學習
上圖中有EClass 、EAttribute 、EReference 、EDataType 四個類,這四個類都是元模型(位於MOF的M2層)。它們的模型又都是EClass,因此位於MOF的M3層的只有EClass ,因此EClass 也是元元模型,可參考下圖:開發工具
須要四種Ecore類來表示咱們的模型:
1. EClass 用於表示模型中的類,它有一個name,0個或多個attributes,0個或多個references。
2. EAttribute 用於表示模型中的attribute,它有一個name和一個type。
3. EReference 用於表示兩個類之間的關聯,它有一個name,一個布爾值表示它是不是containment,還有一個引用類型(其它類)。
4. EDataType 用於表示attribute的類型,它能夠是基本類型,例如int 、 float 或對象類型 java.util.Date等。
從圖中能夠注意到Ecore類的名稱和UML中的很是類似,這並不奇怪,由於UML是統一建模語言。事實上,你也許會疑惑爲何EMF模型不是UML呢?EMF爲何還須要本身專門的Ecore模型?緣由就是,Ecore是UML的簡化子集。
如今咱們可使用定義在Ecore中的類的實例,來描述應用程序模型中的類結構。
你能夠從你開始的任何輸入形式中創建模型。若是從Java接口開始,EMF將分析並創建Ecore模型。若是從XML Schema開始,模型也將從中創建。若是從UML開始,將有三種可能性:
1. 直接Ecore編輯。EMF有一個簡單的基於樹的樣本編輯器。
2. 從UML導入。EMF Project和EMF Model 嚮導(wizard)提供一個可擴展的框架,其中有模型導入器,支持不一樣的模型格式。
3. 從UML導出。相似於第二種選擇,可是轉換支持是由UML工具專門提供的。
也許你會想,第一種選擇是最好的,由於在開發過程當中不須要導入和導出的步驟,也沒必要擔憂Ecore模型與工具自己的模型不一樣步。
第二種和第三種選擇的優勢是,相比於EMF建模,你可使用UML工具作得更多。
如今你也許會想Ecore模型的序列化形式是什麼。上文中,這個「概念上的」模型已經被三種物理空間(Java代碼、XML Schema、UML圖)所表示。事實上,咱們有另外一種(即第四種)保存形式來做爲權威性表示: XML Metadata Interchange (XMI)。
爲何不適用前三種形式中的一種呢?首先,Java代碼、XML Schema、UML圖都具備Ecore模型不須要的額外信息,而後,它們三個中沒有一個能夠知足全部EMF使用的場合。採購清單模型的Ecore XMI文件以下:
<?xml version="1.0" encoding="UTF-8"?> <ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="po" nsURI="http://www.example.com/SimplePO" nsPrefix="po"> <eClassifiers xsi:type="ecore:EClass" name="PurchaseOrder"> <eStructuralFeatures xsi:type="ecore:EAttribute" name="shipTo" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> <eStructuralFeatures xsi:type="ecore:EAttribute" name="billTo" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> <eStructuralFeatures xsi:type="ecore:EReference" name="items" upperBound="-1" eType="#//Item" containment="true"/> </eClassifiers> <eClassifiers xsi:type="ecore:EClass" name="Item"> <eStructuralFeatures xsi:type="ecore:EAttribute" name="productName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> <eStructuralFeatures xsi:type="ecore:EAttribute" name="quantity" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/> <eStructuralFeatures xsi:type="ecore:EAttribute" name="price" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloat"/> </eClassifiers> </ecore:EPackage>
要導出EMF模型時,實際上就是導出EMF的XMI。
EMF能夠根據包含標準get()方法的接口,來生成模型屬性和引用。可是,EMF不會盲目地將每個接口和方法都看做模型中的一部分。只有按照EMF特定的規範才能夠生成(EMF使用JavaBeans簡單屬性訪問器命名模式的子集,具體規範可參考http://java.sun.com/products/...)。
根據此規範,須要用@model標明須要用EMF生成模型的接口。例如前面給的PurchaseOrder接口就須要用下面的格式來生成UML:
/** * @model */ public interface PurchaseOrder { /** * @model */ String getShipTo(); /** * @model */ String getBillTo(); /** * @model type="Item" containment="true" */ List getItems(); }
@model標籤是來將PurchaseOrder 標識爲一個須要被建模的類。
shipTo和billTo的類型是String,因此在@model標籤以後沒有額外的模型信息。
getItems()返回的是一個對象類型爲Item的List,因此須要在@model標籤以後加上type="Item"。此外,由於getItems()是貨物的容器並會在其中將貨物做爲孩子序列化,因此須要標識出containment="true"。
從Java 5.0 開始,泛型能夠被用來指定List中對象的類型,從EMF 2.3開始,也能夠支持泛型。
咱們注意獲得在接口PurchaseOrder中沒有定義 setShipTo()和setBillTo()方法,由於在EMF看來,只要get()方法的上面有註釋,若是沒有相應的set()方法,EMF就會自動生成set()方法並整合到接口中。
咱們回顧一下前面的知識。
- Ecore和XMI序列化,是EMF的核心。
- Ecore模型的建立,有至少三種方式:UML模型、XML Schema、註釋後的Java接口。
- Ecore模型能夠生成 Java 接口的實現代碼以及模型的其餘形式。
使用XML Schema來定義模型有個重要的優勢:根據schema,能夠序列化模型的實例來符合模型。除了簡單地定義模型,XML Schema也能指定模型實例的持久形式。
這裏有個問題:「是否有其餘持久形式?」答案是確定的。例如RDB Schema。「藍圖」以下:
EMF最大的優勢就是自動生成代碼的效率很高。如今,在前面的基礎上,你只須要使用EMF Project 嚮導(自動加載了生成器)來建立項目,而後在菜單上選擇Generate Model Code。
EMF生成的代碼是什麼樣的呢?
首先,Ecore類(好比一個EClass)對應Java中的接口以及其實現類。舉例子來講,PurchaseOrder的EClass對應的Java接口:
public interface PurchaseOrder ...
這個接口對應的實現類:
public class PurchaseOrderImpl extends ... implements PurchaseOrder {
這種接口-實現二者分離(interface–implementation)是EMF青睞的設計選擇。由於這是任何一個類模型API(model-like API)最好的模式。例如,文檔對象模式(DOM)是這樣的,Eclipse的許多API也是如此。它也是支持Java中多重繼承的必要模式。
其次,每一個生成的接口都直接或間接擴展了基接口EObject:
public interface PurchaseOrder extends EObject {
EMF中的EObject等價於java.lang.Object,它是全部建模對象的基礎。擴展的EObject引入瞭如下三種主要行爲:
1. eClass()返回對象的元對象(一個EClass)。
2. eContainer()和eResource() 返回的是對象裏面包含的對象和資源。
3. eGet()、eSet()、eIsSet(),和eUnset()提供一個API,用來反射式訪問對象。
而後,EObject仍是另外一個接口Notifier的擴展:
public interface EObject extends Notifier {
Notifier接口向建模對象中引入一個重要的特性:模型變動通知(notification),正如在觀察者模式(Observer design pattern)中。和對象持久性同樣,通知也是EMF對象的一個重要特徵。
最後,根據類型及用戶設定的屬性生成方法以及方法中的實例變量。例如:
public String getShipTo() { return shipTo; }
EMF會自動生成對應的set()方法並設置相同的變量,可是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)); }
能夠看到,爲了使方法更高效,當沒有觀察者時,爲了不調用eNotify()花費的高昂代價,EMF會添加一個eNotificationRequired()守衛條件。