深入淺出Eclipse Modeling Framework (EMF)


  Eclipse Modeling Framework (EMF),簡單的說,就是Eclipse提供的一套建模框架,可以用EMF建立自己的UML模型,設計模型的XML格式或編寫模型的java代碼。EMF提供了一套方便的機制,實現了功能的相互轉換,大大提高了效率,下面是其具體功能介紹:

一、統一java、UML、XML

  為了幫助理解EMF,我們可以舉一個例子。假設老板要你寫需要寫一個程序,來管理供應商的采購清單,采購清單需要維護三大項:付款方(bill to)、運送地址(ship to)和購買貨物集合(集合中包含名字name、數量quantity、價格price)。你回答說:沒問題。然后就開始設計采購系統的java接口:

 1 public interface PurchaseOrder  2 {  3   String getShipTo();  4   void setShipTo(String value);  5   String getBillTo();  6   void setBillTo(String value);  7   List getItems(); // List of Item
 8 }
9 public interface Item 10 { 11   String getProductName(); 12   void setProductName(String value); 13   int getQuantity(); 14   void setQuantity(int value); 15   float getPrice(); 16   void setPrice(float value); 17 }

  然后就准備開始實現自己的接口,忽然老板問了一句,“你不要先建立自己的模型嗎”,對於大多數從來不建模的java程序員,你認為代碼就是模型,用那些形式化建模元素建立模型除了能在文檔中增加篇幅外,沒有任何好處,但即使如此,你仍然要聽老板的話,所以你建立了如圖1所示的UML模型。

圖1 采購系統的UML模型

  這里,你可以要求老板走開,然后編碼實現了,但首先你還得保存模型,這里我們你想到了用XML文件保存,自詡聰明之時,寫好了如下的XML schema文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 3        xmlns:po="http://www.example.com/SimplePO"
 4        targetNamespace="http://www.example.com/SimplePO">
 5   <xsd:complexType name="PurchaseOrder">
 6     <xsd:sequence>
 7       <xsd:element name="shipTo" type="xsd:string"/>
 8       <xsd:element name="billTo" type="xsd:string"/>
 9       <xsd:element name="items" type="po:Item"
10               minOccurs="0" maxOccurs="unbounded"/>
11     </xsd:sequence>
12   </xsd:complexType>
13   <xsd:complexType name="Item">
14     <xsd:sequence>
15       <xsd:element name="productName" type="xsd:string"/>
16       <xsd:element name="quantity" type="xsd:int"/>
17       <xsd:element name="price" type="xsd:float"/>
18     </xsd:sequence>
19   </xsd:complexType>
20 </xsd:schema>

  在下一步工作之前,你突然意識到現在已經有三種系統表示:Java interfaces、UML模型和 XML Schema,你開始思考,同一個系統設計了三種模型(暫且認為XML和接口也是模型),進行了大量重復的工作,有沒有一種方法,只設計其中的一種模型,然后實現三者之間的相互轉換?

  此時,EMF出現了,EMF是一個建模框架,能夠實現java代碼的生成,即它統一了三種重要的技術:Java, XML 和 UML。如圖2所示。

圖2 EMF 統一了UML、XML、java

  假設你要操作一個XML文檔,如果你是XML schema大牛,你可以從設計schema開始,然后通過EMF得到UML模型,再通過EMF得到java代碼,當然如果你不是,可以從設計UML模型開始,一樣可以完成上述功能。

二、建模 vs 編程

  這里有的人可能會問:難道EMF只是一個描述模型和生成其它東西的框架嗎?回答是的,但不全面,EMF的功能遠不止此,它能有效地為編程服務,這也是一個經典問題的答案:“我是應該先建模還是直接寫代碼?”,EMF的回答是兩者都可以,因此在EMF看來,兩者是等價的。用英語回答就是:"To model or to program, that is not the question."

三、定義模型

  上面我們用三種形式描述了我們的概念模型,那么這三者有沒有共同的模型概念,方便它們之間的轉換呢(模型轉換的知識在此暫時省略)?我們再回顧下前面的三種模型:

  • PurchaseOrder and Item 在UML和java中是類,但在XML schema中是復雜類型定義;
  • shipTo, billTo, productName, quantity 和 price在UML中是屬性,在java中是get()/set()方法對,在XML schema中是內嵌的元素類型;
  • items在UML是類的關聯或引用,在java中是get()方法,在XML schema中是另一種復雜類型的內嵌元素類型。

  可以看到這三者都用到了一種較高層的定義來分別表達UML、XML、java。因此,要實現EMF和模型之間的轉換,我們需要這種能夠描述EMF模型的模型,我們稱為元模型。

1、Ecore (Meta)模型

  能夠描述EMF的模型稱為Ecore元模型(位於MOF的M2層,關於MOF可以參考http://en.wikipedia.org/wiki/Meta-Object_Facility),它本身也是EMF模型,因此Ecore是它自己的元模型。如果再深入一點,Ecore的元模型又是什么呢?回答仍然是Ecore,因為它可以描述自身。Ecore只是OMG的MOF在Eclipse下的一種實現,可能還有其它元模型形式(可以參考http://wenku.baidu.com/view/d28de6717fd5360cba1adb28.html第二段),這里就不詳細介紹了。圖3給出了一個簡化的Ecore元模型,說它是簡化的,是因為它只是Ecore元模型的子集,而且為了方便,將某些公共類省略,如ENamedElement類(這個類定義了類中屬性的名字)。

圖3 簡化的Ecore元模型

  從圖2中可以看到,我們需要四種Ecore類來描述我們的模型,它們是:

  • EClass 用於表示模型中的類,它有一個name,0個或多個attributes,0個或多個references。
  • EAttribute 用於表示模型中的attribute,它有一個name和一個type。
  • EReference 用於表示兩個類之間的關聯,它有一個name,一個布爾值表示它是否是containment,還有一個引用類型(其它類)。
  • EDataType 用於表示attribute的類型,它可以是基本類型,例如int 、 float 或對象類型 java.util.Date等.

  我們還可以觀察到,Ecore元模型這么類似於UML中的類圖,這不足為怪,因為UML本來就是一種統一的建模語言(名符其實),倒是有另一點可能不明白?即然Ecore也是UML,為什么不用UML作為UML的元模型呢?答案很簡單,因為Ecore只是UML的子集,沒有必要用到除類圖以外的其它UML元素。

  有了Ecore元模型,我們就可以用我們的例子實例化Ecore元模型,圖4是采購系統的Ecore實例(如果對此不理解,可以參考http://www.cnblogs.com/riky/archive/2007/04/07/704298.html)。

圖4 采購系統的Ecore實例

2、XMI序列格式

  有了Ecore元模型后,可能有人會問:“Ecore的序列化格式(Serialized form)是什么呢?”是Java code、XML Schema 和 UML diagram中的一種嗎?實際上,Ecore的規范序列化格式是XMI(XML Metadata Interchange),為什么會出現另外一種XMI格式,而不用前面介紹的三種格式呢?首先,Java code、XML Schema 和 UML diagram都增加了Ecore本身並不具有的信息,已經改變了Ecore;其次,Java code、XML Schema 和 UML diagram中沒有一種能夠適用於任何場合,即沒有一種是通用的,而XMI正好滿足這兩點。我們的采購系統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。

 3、java注釋

  我們已經知道EMF能夠將java接口自動生成UML的屬性和方法,但是否所有的接口都能生成呢?或者什么樣的接口才能生成呢?首先,EMF並不會盲目地把所有的java接口自動生成UML的屬性和方法,其次,只有符合特定規范才能通過EMF生成(EMF使用的是JavaBeans簡單屬性祖先命名模式的子集,具體規范可參考http://java.sun.com/products/javabeans/docs/spec.html)。根據此規范,需要用@model標明哪些接口需要用EMF生成模型。例如前面給的PurchaseOrder接口就需要用下面的格式來生成UML:

/** * @model  */
public interface PurchaseOrder {  /** * @model  */   String getShipTo();  /** * @model  */   String getBillTo();  /** * @model type="Item" containment="true"  */   List getItems(); }

 

4、Ecore "Big Picture"

  我們先來回顧前面的知識:

  • Ecore和它的XMI序列化格式是EMF的核心;
  • EMF模型至少可以通過三種形式得到(UML、XML schema、Java 接口);
  • 通過Ecore模型可以生成Java接口及其它形式的模型;

  其實,通過XML schema定義模型有一個優勢:給定schema后,可以序列化成持久模型的實例。因為XML schema不僅僅定義了模型,還指定了模型實例的持久格式。問題出來了:是否還有其它的持久模型格式呢?回答是肯定的,例如relational database (RDB) Schema等。Ecore的"big picture"如圖5所示。

圖5 Ecore及其源格式

四、生成代碼

  EMF最主要的功能莫過於其自動代碼生成能力了,能夠極大提高工作效率。假設我們已經有了上面的模型,生成轉換成Java代碼呢?你只要新建EMF項目,它即自動加載了生成器。那么,EMF生成是的是什么樣的代碼呢?

  首先,Ecore類(即EClass)相當於java的兩個實體:接口及其實現類。例如對於EClass PurchaseOrder相當於java的:

public interface PurchaseOrder ... public class PurchaseOrderImpl extends ... implements PurchaseOrder {

 

  是這種設計方式,是因為我們認為這是類模型API(如文檔對象模型DOM)的最好模式。

  其次,每個生成的接口都直接或間接擴展了基接口EObject,如:

public interface PurchaseOrder extends EObject {

 

  EObject是java.lang.Object的EMF等價物,即所有模型對象的基礎。擴展的EObject包含下面三種行為:

  • eClass()返回對象的元對象(是一個EClass)。
  • eContainer() and eResource() 分別返回對象的包含對象及其資源。
  • eGet(), eSet(), eIsSet(), and eUnset() 提供了訪問交互對象的API。

  然后,EObject還是另一個接口的擴展:

public interface EObject extends Notifier {

 

  最后,根據類型及用戶設定的屬性生成代碼。例如:

public String getShipTo() {   return 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)); }

 

  注意到當沒有觀察者時,為了避免調用eNotify()花費的高昂代價,EMF會添加一個eNotificationRequired()守衛條件。

五、總結

  本篇由淺入深的介紹了EMF的相關知識,由於篇幅原因,不可能將全部的EMF知識都寫在這里,感興趣的讀者可以通過參考書籍了解其它內容。

六、參考資料

  1. Steinberg, D., Budinsky, F., Paternostro, M., Merks, E.: Eclipse Modeling Framework, 2nd Edition. Pearson Education (2008)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM