早期的图形化界面应用程序主要使用GEF框架来开发,如:JBPM流程定义工具,采用GEF+javaBean的组合模式,这种方式不好的一点就是在模型层的处理上过于繁琐,需要开发人员人为实现模型通知机制,而且也不利于后期的扩展维护,一旦有新需求出现,修改模型结构非常困难。
于是有人尝试EMF+GEF的组合模式,因为EMF在模型映射处理上相对比较成熟,而且由EMF所驱动出的实体类本身也具备了通知机制,可以将模型结构的状态改变通知到控制层,但在使用的过程中也有新的难点出现,EMF和GEF分别有自己的命令处理机制来用于定义对模型的修改操作,两套机制所定义的接口不同,没有办法实现通用。
GMF框架的设计解决了EMF和GEF整合的困难,通过适配的方式将两种命令机制关联起来,并且在整合两个框架的同时,也做了很多功能上的扩展,包括:
1、从Ecore元模型中扩展出Notation Model(修饰模型)用于定义节点的修饰(字体、坐标、布局约束等),从而将节点的修饰属性从业务模型中独立出来,让Ecore模型只关注在业务逻辑的建模处理上。
2、GEF框架的视图展现主要使用了Java的draw2D技术框架,需要开发人员手工编写相应的Figure类来定义视图节点的显示,GMF框架对视图的展现做了进一步的封装处理,将视图的定义抽象到模型配置文件中去进行管理,在由Runtime子框架去解析生成相应的Figure实体,从而让开发人员以定义模型的方式来定义视图。
GMF框架是一种典型的模型驱动开发方法,开发人员甚至可以在不编码的情况下完成模型实体到上层应用的整个驱动过程,但是由它所驱动出的项目实例只是针对大众化需求的一个功能定制,如果想加入个性化需求,修改工程代码也是在所难免的。新建一个GMF项目,我们要走以下这样一个流程:
既然是模型驱动开发方法,肯定离不开模型的定义
首先借助EMF框架来完成Domain Model的建模处理 生成模型的配置文件 *.ecore;
然后定义视图模型,视图的展现主要包含两部分,分别是Figure节点和用于创建Figure的工具,两个模型分别保存到*gmfgraph文件和*.gmftool文件当中;
直到现在这三个模型在应用上还是处于相互独立的状态,需要有一个模型来将他们关联起来,以达到级联操作的效果,这个模型就是映射模型(Mapping Model);
有了映射模型,GMF的模型定义操作已经完成一大半,剩下的就是通过映射模型来生成Generator模型,而我们项目的代码最终是由Generator模型来驱动生成的。
在由映射模型得到Generator模型的过程中需要用到EMF的genmodel模型,我们都知道EMF是基于两种元模型来构建的,Ecore模型
主要定义了模型的基本结构,而genmodel模型主要配置了有关代码生成方面的相关信息,Generator模型同样需要genmodel模型属性配置来用于代码的生成。
接下来我们以实践的方式来演示整个操作流程
首先使用EMF框架进行业务领域建模,模型结构大致如下:
该模型的主要功能是效仿JBPM流程定义工具,并在功能上稍做扩展,使用过JBPM的人都知道,在JBPM自带的流程编辑器里,任务分配器的编写是不能以图形化界面的操作形式来展现的,需要开发人员手工修改对应的XML文件,而我们所要做的扩展功能就是可以让用户以拖拽的方式来为任务指定分配器。
目前已定义的分配器包括两种:
PersonAssignment表示分配到人;
PositionAssignment表示分配到岗位;
其他节点都是效仿JBPM常用标签进行定义的,Start表示开始节点,End表示结束,Task表示任务,Transition表示连接,因为只是实例的演示,所以没有把JBPM的标签全部迁移过来,只是定义了几种比较常用的。
另外需要注意一点的是,使用EMF进行建模处理一定可以对外提供一个根对象用来表示整个模型(比如这里的Process对象表示整个流程实体),而其他对象一定可以通过组合的方式直接或间接添加到根对象之中,或者说实体类层级之间的引用关系一定要设计成组合模式,这样EMF框架才可将这些资源实体保存到同一个资源文件中去。
有了Ecore模型之后,可以基于该模型创建genmodel模型,正如前面强调的Ecore模型定义了模型的基本结构,而genmodel模型主要配置有关代码生成方面的相关信息,经常修改的几个属性包括:
Base Package:表示EMF所生成的代码默认保存在以哪个路径为基础的包当中;
Runtime Platform:模型所驱动出的项目实例以什么样的方式运行,IDE-以插件的形式运行;RCP-以Application形式独立运行。我们暂时把他设置成以插件的形式运行。
按照流程,业务模型建好之后,我们需要建立视图模型,首先建立graphic model,在向导界面中选择Graphicl Modeling Framework路径下的Simple Graphical Definition Model,在接下来的向导页中Diagram Element对象一定要选择Ecore模型所对外提供的根对象,这里也就是process对象
在接下来的选项页中,为了演示视图模型的定义操作,去除所有的选项,建立模型配置文件之后,我们会以手工的方式来定义这
些视图元件。
模型建立好之后,文件的层级结构是这样的,首先模型的根节点是Canvas对象,表示一个画布,其他视图节点都是绘制于该画布之上,然后Canvas节点包含了一个Figure Gallery子节点,该节点的功能主要用于声明Figure的描述信息。在这里我们新建一个Figure Descriptor子节点,将Name属性设置成TaskFigure,表示该Figure是用于描述Task视图的,然后为该节点添加一个Rounded Rectangle子节点,表示Task视图是一个圆角矩形,在属性视图里可以对它的属性信息做一些修改:
Corner:表示圆角的弧度,Fill表示是否填充,Line Kind表示轮廓类型,Line Width表示轮廓线的宽度,outline表示是否显示轮廓线,可根据需求自行调整。
接着为其添加一个Flow Layout子节点,来为该圆角矩形指定布局信息:
vertical表示是否是垂直方向布局,Force Single Line表示是否布局到一条线上,Match Minor Size表示是否匹配最小宽度或高度,难于理解的可能是Major和Minor两个词的含义,其实它们所代表的意识是和Vertical属性值有关的,拿Major Alignment来讲,如果Vertical属性为true则Major Alignment表示垂直方向对齐方式,相反,如果Vertical属性设置为false,则Major Alignment表示水平方向对齐方式。
除了有圆角矩形之外,我们还需要在矩形上绘制一个Label用来显示任务名称,实现方法是为Rounded Rectangle节点添加Label子节点,这样一个任务节点的视图就定义好了。
然而我们的模型定义并没有结束,就像刚开始提到的,Figure Gallery节点只是定义了视图组件的描述信息,所声明的这些组件并没有绘制到Canvas上去,换个角度说,Figure Gallery节点只是声明了一些组件类,这些类要想拿来使用还有一个实例化的过程。怎样将这些组件类实例化呢?
在Canvas节点下新建一个Node节点,Name属性设置成Task,Figure属性设置成我们刚刚定义的TaskFigure,这样就将一个TaskFigure的实例绘制到Canvas组件上了,同样,我们还需要把TaskLabelFigure也绘制到Canvas组件上,新建一个Diagram Label子节点,Figure属性同样选择TaskFigure,所不同的是我们还要为它指定Accessor属性,Accessor属性相当于一个方法,可以通过它来获取定义的组件,在TaskFigure下面新建一个Child Access子节点,Figure属性选择TaskLabelFigure,然后将新建的Child Access赋予Accessor属性,这样一个简单的视图模型就创建完毕了。
按照流程,我们继续创建tool model
在Graphical Modeling Framwork路径下面选择Simple Tooling Definition Model,Diagram Element选项中,同样选择Ecore模型所对外提供的跟对象Process,向导结束,模型结构大致如下:
Tool模型主要定义了创建Figure所需要的一些工具,这些工具由统一的调色板(也就是Palette节点)来进行管理,而其子节点Tool Group用于定义工具组,可以将具有相似功能的工具添加到同一个组中来进行管理,这里,我们新建一个工具(Creation Tool)用于绘制Task Figure,设置Title属性为"任务",Description属性为"创建任务",然后为该工具指定图标信息,分别添加Small Icon Default Image子节点和Large Icon Default Image子节点,这样,一个简单的工具模型也创建好了。
接下来所需要做的任务就是将把3个在功能上相互独立的模型映射关联起来,创建映射模型。
在Graphical Modeling Framwork下面选择Guide Mapping Model Creation,然后依次选择已创建的3个模型,在向导页的最后一页,移除所有Links选项和Nodes选项(因为我们要以手工的方式来演示映射模型的配置过程),点击Finish模型创建完毕。
接下来我们所需要做的工作是将Ecore模型的Task节点,gmfgraph模型的TakFigure和gmftool模型中的工具节点相互关联起来。首先在Mapping节点下面新建一个Top Node Reference子节点,将Containment Feature属性设置成Process.nodes,注:这里一定要是ContaimentFeature,因为Process和Node之间的关系是组合关系,而不是简单的对象引用关系;然后为其添加Node Mapping节点,将Element属性设置成Task,DIagram Node属性设置成TaskFigure,Tool属性设置成"Creation Tool任务",这样3个模型所定义的节点元素就相互关联起来了。
最后为Task节点添加name属性到TaskLabelFigure的映射,新建一个Feature Label Mapping子节点,设置Features to display属性为Node.name,Diagram Label属性为Diagram Label TaskName,点击保存,映射模型创建完毕。
最后一步,由映射模型生成generator模型
右键映射模型文件,选择create generator model,依次选择创建好的映射模型和genmodel模型,最终得到generator模型,有了该模型之后,我们便可基于模型来驱动出项目代码。
首先借助与EMF的genmodel模型来生成与模型相对应的实体类和适配器类,然后右键process.gemgen,选择generate diagram code,GMF便会生成我们整个编辑器的上层应用。