为了理解EMF到底是什么,你只须要知道一件事:“模型”(model)是什么?“模型”的目的是什么?html
EMF不要求全新的方法论亦或是任何复杂的建模工具。只须要从Eclipse的Java开发工具着手开始。java
EMF将建模概念直接与其实现相关联,因此上手比较容易。程序员
举个编程实例,假设老板让你编写一个程序来管理供应商的采购清单。采购清单包含付款对象(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文件的结构: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的。编辑器
有个常常问到的问题:“我是该建模呢仍是该编程?”,它的答案是:“均可以,建模亦或编程,这并非问题。”在EMF看来。建模和编程能够被认为是等价的。
对于初学者,相比于代码,建模更容易使他们描述出应用程序的功能。若是你是个经验丰富的程序员,若是对高阶建模的想法没有很大的信心,可将EMF看做建模的文雅介绍以及蕴含的优点。你没必要接触一种全新的方法论,但你也能享受的建模的一些好处。
若是你已经了解了建模的知识,甚至是MDA的重点,你应该讲EMF看做在那个方向的一种技术。问题是高阶建模语言还须要去学习,此外,由于咱们将须要处理(例如调试)生成的Java代码,因此还须要理解它们之间的映射。优秀的传统Java编程是作这件事最简单和最直接的方式。
咱们认为,EMF将建模与编程完美地相结合,以最大限度地发挥二者的效果。
在EMF看来,用户和其余开发者没必要去了解高阶建模语言和生成的Java代码之间的映射,这些映射让Java程序员来理解是天然而又简单的。同时,应用程序之间的细粒度数据集成;代码生成产生的生产力增益,这些是建模的优点。工具
用于表示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工具作得更多。
如今你也许会想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。
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和XMI序列化,是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()守卫条件。