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

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

    
    
        
            
                
                
                
            
        
        
        
            
                
                
                
            
        
    

  在进行下一步工作前,你意识到,针对同一事物:程序的“数据模型”,你已经拥有三种不同的表示。于是你在思考:能不能只编写三者中的一种模型,其他两种从这一种模型中生成?更进一步,在这种模型中是否有足够的信息来生成接口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文件如下:



  
    
    
    
  
  
    
    
    
  

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

你可能感兴趣的:(uml,eclipse插件,xml,java)