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()守卫条件。

相关文章
相关标签/搜索