What every Eclipse developer should know about EMF
翻译自:https://eclipsesource.com/blogs/tutorials/emf-tutorial/
本教程是对EMF的介绍,解释了EMF的基础知识。我们首先向您展示如何基于EMF构建一个非常简单的以数据为中心的应用程序,包括UI。我们解释如何在EMF中定义模型并从中生成代码。我们探索生成代码的API,即如何创建、导航和修改模型实例。
接下来,我们演示如何使用数据绑定构建基于该模型的UI。对于我们的示例,我们构建了一个应用程序来管理保龄球联盟(a bowling league),包括比赛和球员。在本教程的后面,我们将探索使用AdapterFactories的优势,并简要介绍EMF中的数据管理。我们还包括一些关于EMF最重要的附加技术的要点。如果您对快速获得基于EMF构建应用程序的结果感兴趣,也许可以EMF客户端平台对你来说也是一个好的起点,明白吗本教程.
PDF下载:本教程也可以PDF格式下载在我们网站上.
安装要求:为了完成这些示例,您需要安装EclipseEclipse下载页面.
介绍
为了回答这个问题,“什么是EMF?”,我们将借用EMF网站上的描述:
“EMF项目是一个建模框架和代码生成工具,用于基于结构化数据模型构建工具和其他应用程序。根据XMI描述的模型规范,EMF提供了工具和运行时支持来为模型生成一组Java类,以及一组适配器类(支持查看和基于命令编辑模型)和一个基本编辑器
来源:https://www.eclipse.org/emf
值得一提的是,除了作为一个成功的建模框架外,EMF还一直是许多其他建模技术的稳定标准。我们建议对您想在Eclipse中创建的任何结构化数据模型使用EMF,特别是如果它是在UIs中存储、显示和修改的。
基本的EMF工作流程非常实用;模型是以Ecore格式创建和定义的,它基本上是UML类图的一个子集。从Ecore模型中,您可以生成Java代码。
在本教程的后面部分,我们将有两个正在运行的Eclipse实例。在第一个实例中,称为“IDE”,我们将定义模型并从中生成代码。第二个实例称为“运行时”,将从IDE中启动,并将包含所生成模型的实例。
示例模型
在本教程中,我们将创建一个管理保龄球联盟及其锦标赛的示例模型。一个联盟包含任意数量的球员。锦标赛由任意数量的比赛组成。每场比赛总是包含两场比赛。一场比赛是一个框架列表(分数),并分配给特定的球员。最后,锦标赛有一个确定锦标赛类型的枚举。
在下一节中,我们将展示如何从该模型创建和生成代码。
建模
我们将在EMF中创建示例模型,为我们的应用程序生成实体类。第一步是在您的工作区中创建一个空的建模项目。在运行的IDE中,从工具栏菜单中选择“文件”→“新建”→“其他…”并选择“空EMF项目”。
点击“下一步”,输入项目名称,例如“org . eclipse . example . bowling model”,然后点击“完成”:
建模项目的基本部分是模型本身,以“Ecore”格式定义。请右键单击新建模项目中的模型文件夹→“新建”→“其他…”→“Ecore模型”→“下一步”,并将Ecore文件命名为“bowling.ecore”。
单击“完成”创建模型。它将在默认的Ecore编辑器中打开,该编辑器允许在基于树的视图中定义Ecore模型。定义Ecore模型有几个额外的选项,包括图形建模、文本建模、Java注释和从UML工具导入。在本教程中,我们将坚持使用默认编辑器,稍后将简要演示Ecore的图形化编辑器。
在Ecore编辑器树中,您可以通过拖放来创建和删除模型元素以及修改模型的结构。可以在第二个视图中修改模型元素的属性,该视图通过双击或右键单击“显示属性视图”打开。
你需要给你的新模型包装一个名字和一个URI。这将在properties视图中完成。URI用于稍后识别模型。将包命名为“bowling”,将Ns前缀设置为“org.eclipse.example.bowling”,将Ns URI设置为“https://org/eclipse/example/bowling“。
现在我们可以将模型元素定义为根包的子元素。通过右键单击保龄球包→“New Child”→“EClass”创建一个新的EClass,并在新创建的EClass的属性视图中将名称设置为Player
从EClass的上下文菜单中,您可以添加EAttributes和EReferences作为子级。在Player EClass中创建一个eat attribute并为其打开属性视图。EAttribute的属性定义了它的名称、数据类型和其他属性,我们将在本教程的后面介绍这些内容。将名称设置为“name”并将EType指定为“EString”(Java . lang . string)。重复此步骤并添加第二个名为“EDate”类型的“dateOfBirth”的EAttribute。我们在这里使用的惯例是所有类名都以大写字母开头,属性和引用以小写字母开头。
EMF模型通常构建一个结构化的层次结构,即模型元素实例。例如,一个玩家包含在一个特定的容器对象中。这提供了一个树形结构,对于导航和序列化(例如XML)非常有用。这种树结构通常被称为包容树。在我们的模型中,玩家包含在一个联盟中。值得注意的是,这也意味着每个球员都只被一个联盟引用,因此不能属于多个联盟。EMF将自动确保一名球员不会被包含在多个联赛中。如果你将一名球员加入二级联赛,它对原联赛的引用将消失。
创建第二个EClass并将其命名为“联赛”。为了识别联盟,还要创建一个名为“name”的EString属性。下一步是通过右键单击联赛模型元素来创建联赛和球员之间的电子引用。将引用命名为“玩家”。将引用的EType设置为“Player”。由于一个联赛可以包含任意数量的球员,因此将上限设置为“-1”,相当于“许多”。最后,将属性Containment设置为“true”,将EReference定义为包含引用。
我们已经可以从第一次模型迭代中生成代码,这将在下一节中显示。EMF还可以生成一个示例编辑器。使用这个编辑器,您可以创建生成模型的实例,在我们的例子中,是联赛和球员的实例。这允许我们通过创建模型的实例来对模型进行初始测试。然后我们可以在第二次迭代中进一步完善和添加更多的EAttributes和EReferences,从而完成模型。
代码生成
在这一步中,我们将从我们创建的Ecore文件中生成实体。请注意,如果需要更改模型,您将能够再次再生图元。EMF可以处理简单的变化,比如添加模型元素或属性。如果您有复杂的更改,比如将属性移动到另一个类,您将必须迁移模型的现有实例。这是由EDAPT框架支持的。
要生成实体,我们首先必须创建一个生成器模型。这允许您为代码生成配置不属于模型本身的属性。例如,插件和子文件夹也会生成源代码。
右键单击项目中的模型文件夹→“新建”→“其他……”→“EMF Generator Model”→“下一个”,然后输入bowling.genmodel作为文件名。进入下一页,选择“Ecore model”作为模型导入器。点击“下一步”后,选择“浏览工作区……”并选择我们之前创建的bowling.ecore .进入下一个向导页面并选择“完成”。
在生成器模型的根节点中,您可以设置用于生成代码的属性。在生成器模型的树中,我们可以为每个生成的实体设置属性。对于第一次代码生成,我们将使用默认设置。基于生成器模型,我们现在可以生成源代码。EMF允许您为一个定义的模型生成最多四个不同的插件:
- Model:模型包含创建模型实例的所有实体、包和工厂。
- Edit :编辑插件包含在UI中显示模型的提供程序。例如,提供者为每个模型元素提供一个标签,该标签可用于显示带有图标和名称的实体。
- Editor:编辑器插件是一个生成的示例编辑器,用于创建和修改模型的实例。
- Test:测试插件包含为模型编写测试的模板。
要生成插件,右键单击生成器模型的根节点并选择插件。对于我们的教程,请选择“generate all”。
在我们查看生成的代码之前,让我们启动应用程序并创建模型的一个实体。右键单击包含ecore文件的插件并选择“调试为→ Eclipse应用程序”。这将启动一个新的运行时Eclipse。
然后,在运行时中,创建一个名为bowlinginstance的新的空项目(工具栏菜单→“文件”→“新建”→“其他…”→“常规”→“项目”)。
右键单击创建的项目→“新建”→“其他…”EMF模型创建向导示例”→“保龄球模型”→“下一个”,然后输入league.bowling作为名称。该文件将包含我们的模型实例的序列化版本。
选择“League ”作为模型对象。这将设置我们将要创建的模型实例的根对象。
为模型实例生成的编辑器的工作方式与Ecore编辑器类似。可以通过右键单击创建模型元素实例,并且可以在属性视图中修改属性。请给联盟一个名字,并创建两名球员。保存时,所有创建的实例都在XMI文件“league.bowling”中序列化。
模型细化
让我们切换回IDE Eclipse环境,完成模型并重新生成源代码。在第二次模型迭代中,我们将添加不同类型的电子引用以及元素和多属性。首先,将以下类添加到保龄球模型中:
- Tournament 锦标赛
- Matchup 比赛
- Game 比赛
这些类模拟保龄球比赛的结果,并在我们的模型中建立第二棵树。因此,我们从锦标赛到比赛以及从比赛到比赛添加了包容引用。请记住将两个引用的containment属性都设置为true。根据保龄球规则,一场比赛包括两场比赛(每场由一名球员进行)。我们通过将EClass比赛的e reference“games”的上限和下限设置为“2”来对此进行建模。
我们将比赛和游戏之间的引用定义为双向的。这意味着可以从两端导航参考。因此,我们必须从游戏中创建第二个电子引用来匹配并绑定两个电子引用。EMF将负责双向同步。换句话说,向游戏中添加匹配将自动将游戏添加到匹配中。
请添加一个对名为“matchup”的游戏的引用,其类型为“Matchup”。通过将EOpposite设置为EReference“games”,两个e reference都是双向耦合的。请注意,属性“容器”将自动设置为True。
下一步是添加交叉引用。与包含引用相反,交叉引用的模型元素不包含彼此。在我们的模型中,我们添加了一个名为“Player”的从游戏到玩家的交叉引用引用。将容器和包容属性都设置为“false”。现在可以给一名球员分配任意数量的比赛,而这名球员仍然在一个联赛中。
作为最后一个强制步骤,我们将为该类型的锦标赛创建一个枚举。在我们的模型中,锦标赛可以分为“职业”和“业余”两种类型。请通过右键单击根bowling模型包来创建一个EEnum,就像我们创建一个类一样。向此EEnum添加两个EEnum文本。
然后,向EClass锦标赛添加一个EAttribute,将其命名为“type”,并将EType设置为“TournamentType”。
扩展的示例模型包含更多要添加的属性和引用,包括所有基本类型和一些特殊情况,如锦标赛中的多整数属性。如果您愿意,您还可以模拟以下功能:
运动员
- 高度:EDouble
- isProfessional:boolean
比赛 - 框架:EInt,UpperBound = 10
对模型应用复杂的更改后,在Ecore编辑器中右键单击模型根来验证它总是一个好主意。让我们在模型中做一些错误的事情,并将EAttribute“games”(在Matchup中)的下限设置为3。由于上限是2,这个模型没有太大意义。这将通过模型验证来检测——这在普通Java代码中是不可能的。
在这个模型改进之后,我们将重新生成代码以反映我们的更改。再次启动运行时应用程序并创建第二个模型“锦标赛”。增加一场比赛和两场比赛。要将游戏分配给玩家,您必须加载之前创建的“联赛”模型。从菜单“保龄球编辑器”中选择“加载资源”,并选择第一个模型文件。现在在属性视图中将游戏链接到玩家。
为什么这比写POJOs更好?
您可能会问,“为什么我应该使用EMF而不是通过编写简单的POJOs来创建模型?”不考虑快速测试的生成编辑器和EMF可用的所有其他框架的好处,让我们来看看两个非常简单和典型的好处。
在我们查看生成的代码之前(我们将在一分钟内完成),让我们考虑一下我们刚刚生成的代码量。Eclipse metrics插件告诉我们,我们已经生成了1000多个LOC,而只有150个是实用程序类的一部分。即使非常简单的代码也被认为每LOC值1美元。因此,我们仅通过点击一些按钮就赚了1000美元😉
待续