Eclipse Modeling Framework, 2nd Edition. (EMF)学习笔记(一)——EMF介绍

来源:互联网 发布:apache ranger 编辑:程序博客网 时间:2024/06/05 18:48

  • EMF介绍
    • 统一JavaXML和UML
    • 建模vs编程
    • 定义模型
      • Ecore元模型
      • 创建和编辑模型
      • XMI序列化
      • Java注释
      • Ecore蓝图
    • 生成代码
      • 生成的模型类

EMF介绍

  • 为了理解EMF究竟是什么,你只需要知道一件事:“模型”(model)是什么?“模型”的目的是什么?
  • EMF不要求全新的方法论亦或是任何复杂的建模工具。只需要从Eclipse的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模型。

 接下来需要保存这些“模型”,然后你决定使用XML文件来解决,于是写下了XML Schema来定义你XML文件的结构:
    <?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的。

———-

建模vs编程

  有个经常问到的问题:“我是该建模呢还是该编程?”,它的答案是:“都可以,建模亦或编程,这并不是问题。”在EMF看来。建模和编程可以被认为是等价的。
  对于初学者,相比于代码,建模更容易使他们描述出应用程序的功能。如果你是个经验丰富的程序员,如果对高阶建模的想法没有很大的信心,可将EMF看作建模的文雅介绍以及蕴含的优势。你不必接触一种全新的方法论,但你也能享受的建模的一些好处。
  如果你已经了解了建模的知识,甚至是MDA的重点,你应该讲EMF看作在那个方向的一种技术。问题是高阶建模语言还需要去学习,此外,因为我们将需要处理(例如调试)生成的Java代码,所以还需要理解它们之间的映射。优秀的传统Java编程是做这件事最简单和最直接的方式。
  我们认为,EMF将建模与编程完美地相结合,以最大限度地发挥两者的效果。
  在EMF看来,用户和其他开发者不必去了解高阶建模语言和生成的Java代码之间的映射,这些映射让Java程序员来理解是自然而又简单的。同时,应用程序之间的细粒度数据集成;代码生成产生的生产力增益,这些是建模的优势。


定义模型

Ecore(元)模型

  用于表示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工具做得更多。

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/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();    }

  @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。“蓝图”如下:

———-

生成代码

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

原创粉丝点击