Eclipse Modeling Framework, 2nd Edition. (EMF)學習筆記(一)——EMF介紹

EMF介紹

  • 爲了理解EMF到底是什麼,你只須要知道一件事:「模型」(model)是什麼?「模型」的目的是什麼?html

  • EMF不要求全新的方法論亦或是任何複雜的建模工具。只須要從Eclipse的Java開發工具着手開始。java

  • EMF將建模概念直接與其實現相關聯,因此上手比較容易。程序員

統一Java、XML和UML

  舉個編程實例,假設老闆讓你編寫一個程序來管理供應商的採購清單。採購清單包含付款對象(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模型。
clipboard.png
 接下來須要保存這些「模型」,而後你決定使用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的。編輯器

clipboard.png


建模vs編程

  有個常常問到的問題:「我是該建模呢仍是該編程?」,它的答案是:「均可以,建模亦或編程,這並非問題。」在EMF看來。建模和編程能夠被認爲是等價的。
  對於初學者,相比於代碼,建模更容易使他們描述出應用程序的功能。若是你是個經驗豐富的程序員,若是對高階建模的想法沒有很大的信心,可將EMF看做建模的文雅介紹以及蘊含的優點。你沒必要接觸一種全新的方法論,但你也能享受的建模的一些好處。
  若是你已經瞭解了建模的知識,甚至是MDA的重點,你應該講EMF看做在那個方向的一種技術。問題是高階建模語言還須要去學習,此外,由於咱們將須要處理(例如調試)生成的Java代碼,因此還須要理解它們之間的映射。優秀的傳統Java編程是作這件事最簡單和最直接的方式。
  咱們認爲,EMF將建模與編程完美地相結合,以最大限度地發揮二者的效果。
  在EMF看來,用戶和其餘開發者沒必要去了解高階建模語言和生成的Java代碼之間的映射,這些映射讓Java程序員來理解是天然而又簡單的。同時,應用程序之間的細粒度數據集成;代碼生成產生的生產力增益,這些是建模的優點。工具


定義模型

Ecore(元)模型

  用於表示EMF中模型的模型稱爲Ecore。Ecore自己就是EMF模型,所以是它本身的元模型。也能夠說Ecore是個元元模型
  元模型是模型的模型,若是該模型自己是一個元模型,那麼這個元模型實際上就是元元模型。
  元模型的概念也能遞歸到元元元模型(meta-meta-metamodel)等等,可是咱們目前用不到。
  下圖給出了一個簡化的Ecore元模型,說它是簡化的,是由於它只是Ecore元模型的子集,並且爲了方便,將某些公共類省略,如ENamedElement類(定義了類中屬性的名字)。學習

clipboard.png

  上圖中有EClass 、EAttribute 、EReference 、EDataType 四個類,這四個類都是元模型(位於MOFM2層)。它們的模型又都是EClass,因此位於MOF的M3層的只有EClass ,因此EClass 也是元元模型,可參考下圖:開發工具

clipboard.png

  須要四種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中的類的實例,來描述應用程序模型中的類結構。

clipboard.png

建立和編輯模型

  你能夠從你開始的任何輸入形式中創建模型。若是從Java接口開始,EMF將分析並創建Ecore模型。若是從XML Schema開始,模型也將從中創建。若是從UML開始,將有三種可能性:
  1. 直接Ecore編輯。EMF有一個簡單的基於樹的樣本編輯器
  2. 從UML導入。EMF Project和EMF Model 嚮導(wizard)提供一個可擴展的框架,其中有模型導入器,支持不一樣的模型格式。
  3. 從UML導出。相似於第二種選擇,可是轉換支持是由UML工具專門提供的。
  也許你會想,第一種選擇是最好的,由於在開發過程當中不須要導入和導出的步驟,也沒必要擔憂Ecore模型與工具自己的模型不一樣步。
  第二種和第三種選擇的優勢是,相比於EMF建模,你可使用UML工具作得更多。

XMI序列化

  如今你也許會想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。

Java註釋

  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「藍圖」

  咱們回顧一下前面的知識。
  - EcoreXMI序列化,是EMF的核心
  - Ecore模型的建立,有至少三種方式:UML模型XML Schema註釋後的Java接口
  - Ecore模型能夠生成 Java 接口的實現代碼以及模型的其餘形式
  使用XML Schema定義模型有個重要的優勢:根據schema,能夠序列化模型的實例來符合模型。除了簡單地定義模型,XML Schema也能指定模型實例的持久形式
  這裏有個問題:「是否有其餘持久形式?」答案是確定的。例如RDB Schema。「藍圖」以下:

clipboard.png


生成代碼

  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()守衛條件。

相關文章
相關標籤/搜索