前言
GEF 全称 Graphical Editor Framework 。它是一个基于 eclipse 的图形化编辑框架。通过它,开发人员可以方便的以图形化的方式(而非文本的方式)展示和编辑模型。 GEF 是一种很有价值的工具,很多著名的应用都采用了 gef 框架,其中包括现在最流行 的开源工作流 jbpm 的定制工具。 Gef 是按照标准的 mvc 模式实现的,其中还使用到了工厂模式、命令模式, Policy 模式等一些设计模式。
MVC 结构
MVC 是 "Model-View-Controller" 的缩写,即 ” 模式 - 视图 - 控制器 " 。 MVC 结构强制将一个 应用分为模型层、视图层以及控制器。通过三者之间的协调共同响应完成用户的请求,即交互过程。
它们之间的协作关系如图:
上图为 mvc 模式最标准也是最常见的一种形态。从上图可以看出,模型与视图不直接打交道,而是通过控制器,即控制器完全分离了视图与模型。这是 mvc 模式的最大特点之一。
在现实开发中,由于实际需要,开发设计人员通常会对标准的 mvc 模式进行一些修改。屏弃其中的某些特性,而加入新的特性。其中最常见的变化形式如下图所示:
可以看到,控制器并没有完全分离视图与模型。即它不再负责根据模型修改视图,这一过程是让模型与视图双方“面对面”直接进行。这样做虽然增强了视图与模型之间的耦合,但是减轻了控制器的负担。
另外,在通常情况下,为了进一步解耦合,层与层之间的数据通过一种叫做 DTO 即数据传输对象的介质进行传递。 DTO 的时间通常很简单,只有若干属性和属性对应的 get/set 方法,是一个标准的纯 java 对象,即 pojo 。例如基于 mvc 结构的 struts 中的 actionFrom 即为一个 DTO 。
视图
视图层的作用主要有两个:
l 接收用户输入,将输入信息传递给控制层。
l 负责展示数据,它将模型层中的具体数据以一定的形式展示给用户。
模型和视图之间是一个多对多的关系,一个模型可以对应多个视图,一个视图也可以关联多个模型。
控制器
控制层接收到视图层传递过来的请求后,分析请求信息,根据请求内容选择相应的模型,并修改模型的具体数值。
同时当模型发生变化的时候,将会通知控制器,以更新视图。
模型
模型是行为与状态的双重抽象,即封装了数据与方法。当模型发生变化时,相应的视图会根据新的模型数据重新绘制展现内容。
优点
使用 mvc 结构的应用有如下有点:
l 便于分工。 Mvc 机构将程序分为 ’M - V - C’ 三层,这三层各司其职。这样便于分工协作。例如,传统结构的应用将业务逻辑和界面显示混杂在一起,这样美工很难对页面进行美化。而 mvc 结构的应用,将应用分层,视图层只有展现代码便于美工美化页面。
l 提高了代码的可重用性。多个视图能共享一个模型,多个模型也能共享一个视图,提高了代码的可重用性。
l 便于维护更新,由于 mvc 的解耦合特性。使得维护和变更相对变得比较容易。因此可以方便地改变应用程序的业务数据和业务规则,迁移数据库等等。 不会牵一发而动全身。
Gef 中的 mvc 实现
Gef 完全按照标准的 mvc 体系结构完成。
模型
Gef 模型的规约十分宽泛——任何 java 类都可以作为 gef 的模型。也就是说, gef 在模型层是非侵入式的。这一点使得 gef 的应用更加广泛,灵活。
但是为了能够让控制器知道模型的变化,我们需要一种机制——让模型发生变化的时候通知控制器。由于灵活性的需要, gef 并没有把这种机制实现在框架层。但是我们通常会在模型层中添加 PropertyChangeSupport 类型的成员变量,用来触发事件,通知控制器。例如:
public class AbstractModel {
private PropertyChangeSupport listeners = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.addPropertyChangeListener(listener);
}
public void firePropertyChange(String propName, Object oldVal, Object newVal) {
listeners.firePropertyChange(propName, oldVal, newVal);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
listeners.removePropertyChangeListener(listener);
}
}
视图
视图是模型图形方式的表现,它以某种方式展示模型。 Gef 是一个图形编辑框架,所以 gef 的视图主要是将模型以图形化的方式显示出来。虽然任何实现 IFigure
接口的类都可以作为视图,但是 GEF 使用 Draw2D 可视图形( figure )。 Draw2d 是 SWT 平台上的图形库,你可以用它定制属于自己的、不依赖于特定操作系统本地组件的上层组件。
那么视图又是如何显示的呢。这要用到 AbstractEditPartViewer ,它负责将视图安装到同一个 SWT Control 上。
控制器
在 gef 中,控制器又称为编辑部件 , 是模型和视图之间的桥梁,也是 gef 最重要的部分。所有的 Gef 控制器都需要继承 EditPart 类。
根据 mvc 体系结构,控制器的功能主要有两个。
l 监听模型的变化,根据变化修改视图。
l 当用户编辑视图时,把编辑的结果反映到对应得模型中。
创建——工厂模式
Gef 在控制器的生成中,使用了工厂模式。 通常每一个模型都对应一个控制器,所以 gef 要求提供一个 EditPartFactory ,该对象负责通过给定的模型创建与之对应的控制器。
当模型被创建的时候, EditPartFactory 将会被调用,创建出模型对应的控制器。
监听模型变化
控制器需要监听模型的变化,从而根据新的模型数据重新显示视图。那么就需要将控制器注册成为模型的监听者。例如下面的代码
public void activate() {
if (isActive()) {
return;
}
super.activate();
((Node) getModel()).addPropertyChangeListener(this);
}
public void deactivate() {
if (!isActive()) {
return;
}
super.deactivate();
((Node) getModel()).removePropertyChangeListener(this);
}
方法 activate 的作用是当被激活的时候,把自己注册成为模型的监听。 Deactivate 相反,当失活的时候,将自己从对应的模型的监听者中删除。
由于模型层使用了 PropertyChangeSupport 类,所以,控制器需要 实现 PropertyChangeListener 接口。 PropertyChangeListener 要求实现propertyChange 方法。
public interface PropertyChangeListener extends java.util.EventListener {
void propertyChange(PropertyChangeEvent evt);
}
当模型发生变化时,对应控制器的 propertyChange 方法将会被调用。要说明的是,在 gef 应用中,控制器通常负责根据模型数据修改视图显示,而非简单的通知视图重画。
修改模型——策略与命令
当用户进行编辑操作的时候,控制器还需要解析这些操作并修改相应的模型。这通常是一系列负责的操作,可能会使得控制器非常庞大。 Gef 通过进入请求编辑策略与命令模式很好大分担了控制器的本身的负担。也达到了解耦合的目的。其中编辑策略用于解析用户编辑请求,命令用于修改模型。
同时引入命令模式的另外一个目的就是“回退”功能,一个好的图形编辑器“回退”功能是必不可少的。命令模式通过“ redo ”和“ undo ”等操作可以很多好的支持“重做”以及“回退”的功能。
当控制器接收到用户的编辑请求后,会根据请求对象中的角色信息寻找处理该请求的编辑策略。要说明的是,编辑策略需要事先被注册安装到控制器中,并对应相应的角色。角色是 gef 框架抽象出来的一个标识。控制器 EditPart 就是通过角色标识找到处理请求的策略的。
编辑策略接收到请求后,分析请求信息。根据分析的结果选择创建相应的命令,并且初始化命令的相关参数。
由 gef 框架负责执行编辑策略产生的命令。命令将会修改相应的模型。同时 gef 框架会记录下执行的命令,以备“重做”“后退”等功能使用。
用户编辑请求响应过程
综上所述,在 gef 框架中,用户编辑请求的相应过程如下:
1. 当用户进行编辑操作时, gef 框架将操作封装成一个请求对象,并将其传递给控制器。
2. 控制器根据角色选择相应的编辑策略处理请求。
3. 编辑策略分析请求数据,创建并初始化命令对象。
4. 命令执行的过程就是修改模型的过程。
5. 当模型被修改后,将会触发事件,告知其监听者——该模型对应的控制器。
6. 控制器根据模型数据修改视图显示。
结束语
Gef 是一个完全基于 MVC 体系构架搭建的图形编辑框架。正是 MVC 体系构架的进入,使得模型层与视图层解耦合。那么模型层的约束就得到了极大的解放。这意味着 gef 可以适用于更广泛的业务。也正是 MVC 体系结构的进入,使得基于 gef 框架开发的应用结构更加清晰,更加有利于分工开发与后期维护。
同时 gef 引入了请求编辑策略与命令模式成功分解了 gef 控制层的负担。其中命令模式的引入也使得应用支持“重做”“回退”等功能。