EMF介绍系列(六、自定义命令)

简介: EMF生成的应用程序里,用户的发出的每一条命令都是可以撤销(Undo)的,例如修改了产品的价格,按一下撤销按钮就能恢复原来的价格,当然还可 以通过重做(Redo)再回到新的价格。为了实现这个功能,应用程序里维护了一个用于存放命令的类似栈的数据结构(CommandStack),每一条执 行过的命令都被存放在那里,需要撤销时取出最近一条命令进行撤销。

EMF生成的应用程序里,用户的发出的每一条命令都是可以撤销(Undo)的,例如修改了产品的价格,按一下撤销按钮就能恢复原来的价格,当然还可 以通过重做(Redo)再回到新的价格。为了实现这个功能,应用程序里维护了一个用于存放命令的类似栈的数据结构(CommandStack),每一条执 行过的命令都被存放在那里,需要撤销时取出最近一条命令进行撤销。这个数据结构是由EditingDomain对象负责维护的, EditingDomain相当于编辑模型时的环境。

在EMF里命令框架实际上可以分为两大部分,一部分是与模型无关的通用命令,另一部分是.Edit命令,后者是建立在前者的基础之上的。EMF对模 型的任何修改都是通过命令完成的,例如当用户在属性视图里修改一个对象的属性时,会生成一个新的SetCommand实例,然后执行它的execute ()方法,这个方法里对模型进行修改(实际上是通过doExecute()方法),成功执行完成后这个命令被放入命令栈以便撤销时使用。

通用命令可以完全脱离EMF使用,也就是说,这个命令框架可以应用到任何需要命令框架的应用程序中,包括非EMF应用程序。它位于 org.eclipse.emf.common.command包里,其中Command接口定义了什么是“命令“,一个命令具有execute()、 undo()和redo()等方法,还有canExecute()和canUndo()方法用于判断命令是否可被执行或撤销(考虑到资源消耗,有些命令可 能设计为不可撤销更合理)。另一个重要的接口是前面提到过的CommandStack,它的作用是保存所有命令,可以通过 addCommandStackListener()方法注册监听器来观察CommandStack的状态变化。CompoundCommand接口可以 把多个命令按顺序包装成一个组合命令,它具有原子性,类似数据库里事务(Transaction)的概念,只有所有命令都可执行时这个组合命令才可执行, 撤销也是如此。

EMF在.Edit框架提供了针对EMF模型编辑所需要的一些命令(位于org.eclipse.emf.edit.command包),例如 SetCommand用于修改对象的属性,CreateChildCommand的作用是创建一个子元素,还有MoveCommand、 CopyCommand和CutToClipboardCommand等等。这些命令都实现Command接口,并且大部分继承自 AbstractOverrideableCommand这个抽象类,它给我们带来的影响是在Command接口里的方法名前面都加了一个do,比如 execute()变为doExecute()、canUndo()变为doCanUndo()等等,我们在扩展这些.Edit命令时要覆盖doXXX方 法。.Edit命令是通过反射的方式来修改模型的。

EMF提供的这些命令为我们完成基本的模型编辑功能,多数情况下直接使用它们就可以了,但有时通过自定义的命令可以实现一些特别的需求。举个例子来 说,在网上商店的例子里,假设要求产品的价格只精确到小数点后两位,那么我们要在用户输入新价格以后立刻对这个数值进行四舍五入处理,这个操作就可以利用 自定义命令完成。因为利用了.Edit提供的类,所以一般我们应该扩展.Edit命令,具体来说就是SetCommand。

首先通过继承SetCommand创建我们的SetPriceCommand,在这个方法里覆盖doExecute()方法,SetCommand 里有很多可供利用的环境变量,我们要用到的是owner和value这两项,前者是要修改的对象,在这里是产品对象,后者是属性的新值,在这里也就是新价 格。所以我们的SetPriceCommand可以像下面这样写(为了使代码最简,我们直接把EObject类型转换为Product类型,这样就不需要 用反射的方式了):

public   class  SetPriceCommand  extends  SetCommand {
    
public  SetPriceCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, Object value) {
        
super (domain, owner, feature, value);
    }
    
public   void  doExecute() {
        Product product 
=  (Product) owner;
        
double  newPrice  =  ((Double) value).doubleValue();
        newPrice 
=  Math.max( 0 , newPrice); // New price value must >= 0
        newPrice = Math.round(newPrice * 100 ) / 100d; // Max fraction digits is 2    
        product.setPrice(newPrice);
    }
}

要让这个自定义命令生效,必须在ProductItemProvider里覆盖createSetCommand()方法,因为这个方法缺省是返回SetCommand的,我们要在这里做一个判断:如果修改的是价格属性,就返回我们自定义的这个命令,如下所示:

protected  Command createSetCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, Object value,
        
int  index) {
    
if  (feature  ==  ShopPackage.eINSTANCE.getProduct_Price())
        
return   new  SetPriceCommand(domain, owner, feature, value);
    
return   super .createSetCommand(domain, owner, feature, value, index);
}

这样当用户在属性页里改价格属性时,就会调用我们的SetPriceCommand了。顺便说一句,在 GEF 里 也有类似的EditDomain和Command,只是GEF里的Command一般通过EditPolicy的createXXXCommand()方 法来创建。因为GEF和EMF的两套Command机制没有实现统一的接口,所以结合GEF和EMF的时候常会遇到一些问题,需要额外的代码帮助解决,请 参考这两处讨论( 讨论1 , 讨论2 )。

最后要说一句,CreateChildCommand有点特殊,它是.Edit命令但不继承 AbstractOverrideableCommand,而且如果想在创建子元素时自动完成一些工作,不应该通过扩展这个类完成,而应该在 XXXItemProvider的collectNewChildDescriptors()方法里处理,这个方法决定每个对象可以创建哪些子元素,你可 以修改它的代码以对新建的元素做一些处理。

参考资料: Eclipse Modeling Framework A Developers Guide,第3.3节、第14.1节。

点击下载源代码

本文转自博客园八进制的博客,原文链接:EMF介绍系列(六、自定义命令),如需转载请自行联系原博主。

相关文章
|
机器学习/深度学习 数据可视化 算法
【Pytorch神经网络实战案例】23 使用ImagNet的预训练模型识别图片内容
Imagenet数据集是目前深度学习图像领域应用得非常多的一个领域,关于图像分类、定位、检测等研究工作大多基于此数据集展开。
977 0
|
C# 消息中间件 RocketMQ
rocketmq client for c#
基于ikvm的rocketmq的c#客户端,由于阿里对c#不敏感,对这方面的东西缺少。因为工作需要弄了一个,分享给大家 https://github.com/franknew/RocketMQ-Client 如何使用: 1.
5510 0
|
监控 算法 Java
5. GC 调优(基础篇) - GC参考手册
说明: Capacity: 性能,能力,系统容量; 文中翻译为”系统容量“; 意为硬件配置。 您应该已经阅读了前面的章节: 垃圾收集简介 - GC参考手册 Java中的垃圾收集 - GC参考手册 GC 算法(基础篇) - GC参考手册 GC 算法(实现篇) - GC参考手册 GC调优(Tuning Garbage Collection)和其他性能调优是同样的原理。
1039 0
|
13天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
11天前
|
存储 人工智能 搜索推荐
终身学习型智能体
当前人工智能前沿研究的一个重要方向:构建能够自主学习、调用工具、积累经验的小型智能体(Agent)。 我们可以称这种系统为“终身学习型智能体”或“自适应认知代理”。它的设计理念就是: 不靠庞大的内置知识取胜,而是依靠高效的推理能力 + 动态获取知识的能力 + 经验积累机制。
390 134
|
11天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
491 132
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
2天前
|
人工智能 移动开发 自然语言处理
阿里云百炼产品月刊【2025年9月】
本月通义千问模型大升级,新增多模态、语音、视频生成等高性能模型,支持图文理解、端到端视频生成。官网改版上线全新体验中心,推出高代码应用与智能体多模态知识融合,RAG能力增强,助力企业高效部署AI应用。
205 0