EMF介绍系列(三、定制应用程序的基本方法)

简介: 借助EMF的帮助,不用亲自编写一行代码就可以生成一个完整的应用程序,你是不是对EMF有些感兴趣了?不过生成的应用程序看起来都像是从同一个模子里出来的,即 一个多页编辑器,一个大纲视图和属性页,这当然无法满足所有人的需求。

借助EMF的帮助,不用亲自编写一行代码就可以生成一个完整的应用程序,你是不是对EMF有些感兴趣了?不过生成的应用程序看起来都像是从同一个模子里出来的,即 一个多页编辑器,一个大纲视图和属性页,这当然无法满足所有人的需求。不用耽心,只要了解EMF的机制,按照我们的要求修改这个应用程序并不是一件很困难的 事情。

首先大概的看一下EMF为我们生成了哪些东西吧。按照前文的操作,EMF应该一共生成了四个插件项目:com.my.shop、 com.my.shop.edit、com.my.shop.editor和com.my.shop.tests,其中最后一个项目是方便我们编写单元测 试的框架代码,这里我们先不管它,暂时把注意力集中在前三个项目上。

第一个项目是模型部分,主要包含你定义的ecore模型里各类型(EClass,在ecore元模型里类型称为EClass,属性称为 EAttribute)对应的java接口和缺省的实现类代码,例如Product.java和ProductImpl.java,它们分别被放置在 com.my.shop和com.my.shop.impl包里;一个工厂类(ShopFactory)使用工厂模式创建新的模型实例;一个 Package类(ShopPackage)维护关于元模型的信息,提供了一堆静态成员变量;此外还生成了ShopAdapterFactory和 ShopSwitch这两个类,它们是可选的,它们俩的作用这里卖个关子暂时先不说。

第二个项目是.edit部分,这里面包含了一系列ItemProvider类,用来为在jface的各种查看器(Viewer)里显示这些模型对象 提供便利,以CategoryItemProvider为例,它实现了IStructuredItemContentProvider、 ITreeItemContentProvider和IItemLabelProvider这些接口,注意把这些接口名字中的"Item"去掉就是 jface里需要的Provider,可以把这些带有"Item"字样的Provider想象成对jface相应Provider的包装。有了这些 Provider,在应用程序里使用jface的TreeViewer、TableViewer等查看器就很方便了。(前面讲GEF的一个帖子里曾利用 EMF构造模型,当时使用的就仅仅是模型部分,因为并未用到jface查看器。所以视你的应用程序而定,可以只生成模型部分来用,.edit部分依赖模型部分,而反之不然。)

第三个项目是编辑器,这个部分依赖.edit部分,主要包含了几个UI方面的类。EMF为我们生成的这个编辑器有两个用途:一是让我们可以不用从零开始,而是在这个编辑器的基础上进行修改得到自己的编辑器; 二是通过这些代码展示怎样在应用程序里使用前两个项目里的那些类,编辑器包含那么多Tab正是为了演示各种查看器的用法。下面来说一下怎样定制生成的应用 程序。

一、修改ecore模型和genmodel模型

在ecore模型和genmodel模型里我们可以通过修改一些属性改变所生成的代码,例如希望新创建的类别和产品的名称不是空字符串而是" (Unnamed)",就可以在类图里修改NamedElement类的name属性的"Default Value Literal"属性(没错,属性的属性。见图1)。修改ecore模型后,必须更新genmodel模型,方法是在Package Explorer里右键单击shop.genmodel文件,在弹出菜单里选择"Reload...",这样genmodel会从修改后的ecore模型 里获得修改过的信息。之后,再次从genmodel模型生成一遍代码,这样得到的程序运行后,类别和产品的名称缺省就是"(Unnamed)"了。在 genmodel模型里则可以定制更多属性,例如所生成的每个项目的id、生成类所在的包名(本例中为com.my)、类名前缀(本例中为Shop)、是否生成图标、标准属性页里各属性的分类等等。


图1 在EclipseUML类图里修改属性的缺省值

有人对 ecore模型和genmodel模型各自的用途搞不清楚,其实因为JET是直接通过genmodel生成代码的,所以像是否生成图标这些信息放在 genmodel里是比较合适的,而ecore模型里定义的是各个类型以及之间的关系,所以像属性缺省值这样的信息是应该放在ecore里没错的。

二、直接修改生成的代码

前面的方式是让EMF生成我们想要的程序,好处是非常直观和方便,缺点是只有有限的信息可被定制,而一个复杂的应用程序是不太可能仅仅通过填写一些 属性就被生成出来的,所以我们需要代码级的定制。有一个事实你需要知道:无论对ecore还是genmodel模型定制,产生的结果最终都要反映到生成的 代码。让我们对比一下修改名称缺省值前后的代码(因为类图里NamedElement被定义为抽象类,所以这段代码的位置在 CategoryImpl.java和ProductImpl.java里),这是之前生成的代码:

/**
 * The default value of the '{
@link  #getName() <em>Name</em>}' attribute.
 * <!-- begin-user-doc -->
 * <!-- end-user-doc -->
 * 
@see  #getName()
 * @generated
 * @ordered
 
*/
protected   static   final  String NAME_EDEFAULT  =   null ;

下面是定制之后的代码:

/**
 * The default value of the '{
@link  #getName() <em>Name</em>}' attribute.
 * <!-- begin-user-doc -->
 * <!-- end-user-doc -->
 * 
@see  #getName()
 * @generated
 * @ordered
 
*/
protected   static   final  String NAME_EDEFAULT  =   " (Unnamed) " ;

可以看到只是NAME_EDEFAULT的值发生了变化。也就是说,如果我们不修改ecore模型,而是手工修改这段代码也能达到同样的目的。对于熟悉 EMF的开发人员来讲,修改代码很多时候甚至比修改ecore/genmodel模型更加快速(因为省去了reload genmodel和生成代码这两步),但一定要记住,在修改代码的同时修改这部分代码前的javadoc。因为在每次重新生成代码的时候,JET通过原有 代码前的javadoc判断是否覆盖这段代码,如果javadoc里包含一行"@generated"则覆盖,否则保持这部分代码不变。为了让我们手工的 修改不在以后的生成中消失(这可是很大的损失),可以直接删除@generated这一行,更好的办法是将其改为一个统一的字符串,例如 "@generated NOT",以便将来通过javadoc搜索找到自己都在哪些地方做了手工修改。

下面再通过一个例子演示怎样修改EMF生成的代码。假设现在有个需求是让所有价格大于等于100元的产品自动打95折,而小于5元的商品一律按5元算,那么我们需要修改ProductImpl的getPrice()方法,修改前的代码如下:

/**
 * <!-- begin-user-doc -->
 * <!-- end-user-doc -->
 * @generated
 
*/
public   double  getPrice() {
    
return  price;
}

这是修改后的代码,再次提醒一定别忘记修改@generated那行注释:

/**
 * <!-- begin-user-doc -->
 * 所有价格大于等于100元的产品自动打95折,而小于5元的商品一律按5元算。
 * <!-- end-user-doc -->
 * @generated NOT
 
*/
public   double  getPrice() {
    
if  (price  >=   100 )
        
return  price  *   0.95 ;
    
if  (price  <   5 )
        
return  5d;
    
return  price;
}

至于使用哪种方式进行定制,我个人建议能通过修改ecore/genmodel模型解决的问题就不要直接修改代码,尽量减少手工修改的地方有利于保持思路清晰,其实在实际使用中大部分定制还是需要修改代码才能实现的。

点此下载修改后的项目

本文转自博客园八进制的博客,原文链接:EMF介绍系列(三、定制应用程序的基本方法),如需转载请自行联系原博主。

相关文章
|
算法 Java API
Spring Cloud Gateway简单使用
Spring Cloud Gateway简单使用
608 0
|
存储
HBR(Hybrid Backup and Recovery,混合云备份和恢复)是一种备份解决方案
HBR(Hybrid Backup and Recovery,混合云备份和恢复)是一种备份解决方案【1月更文挑战第8天】【1月更文挑战第38篇】
264 5
|
23天前
|
运维 API 开发工具
打造可编程可集成的实时计算平台:阿里云实时计算 Flink被集成能力深度解析
本文由阿里云Flink团队李昊哲主讲,系统介绍Flink四层开放架构:通过OpenAPI、Git集成、多语言SDK等能力,实现控制面、数据面、开发面与运维面的全面开放。助力企业构建可编程、可嵌入、可治理的实时计算平台,推动数据开发工程化升级。
111 1
|
开发工具 git
IDEA中如何使用Git 图文超详细
IDEA中Git使用,实战教程
9800 1
IDEA中如何使用Git 图文超详细
|
2月前
|
存储 缓存 监控
什么是线程池?它的工作原理?
我是小假 期待与你的下一次相遇 ~
213 1
|
9月前
|
人工智能 自然语言处理 搜索推荐
马斯克AI Grok 3 国内如何使用?请收下这篇新手指南!
Grok AI,由埃隆·马斯克(Elon Musk)旗下的人工智能初创公司 xAI 于 2023 年 11 月推出,迅速成为 AI 领域的一颗耀眼新星
3551 80
|
7月前
|
人工智能 自然语言处理 数据可视化
阿里云 Bolt.diy:一键开启全能开发,简单强大零门槛
Bolt.diy是Bolt.new的开源版本,通过自然语言交互简化开发流程,支持全栈开发与二次开发。依托多模态智能调度引擎和主流大模型,实现任务智能匹配、模块化部署及私有模型集成,大幅提升开发效率。平台提供代码自动生成、实时诊断优化与可视化工具,降低开发门槛。体验中发现其简单易用,但存在偶发卡顿问题。总体而言,Bolt.diy是一款高效实用的开发工具,适合新手与企业使用。
348 7
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
2288 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
11月前
|
监控 小程序 前端开发
微信小程序如何安装使用第三方包
本文档详细介绍了微信小程序开发中引入和使用第三方包的步骤。
1395 3
|
12月前
|
机器学习/深度学习 Rust 算法
Python环境管理的新选择:UV和Pixi,高性能Python环境管理方案
近期Python生态系统在包管理领域发生了重要变化,Anaconda调整商业许可证政策,促使社区寻找更开放的解决方案。本文介绍两款新一代Python包管理工具:UV和Pixi。UV用Rust编写,提供高性能依赖解析和项目级环境管理;Pixi基于Conda生态系统,支持conda-forge和PyPI包管理。两者分别适用于高性能需求和深度学习项目,为开发者提供了更多选择。
2585 2