控制器是GEF框架的核心,它负责模型和视图之间的通信。
控制器的功能
在GEF的MVC结构里,控制器(Control)是模型与视图之间的桥梁,也是整个GEF的核心。它不仅要监听模型的变化,当用户编辑视图时,还要把编辑结果反映到模型上。
在GEF中,控制器是由一组EditPart对象共同组成的,每一个模型对象都对应一个EditPart对象。应用程序中需要有一个EditPartFactory对象负责根据给定模型对象创建对应的EditPart对象,这个工厂类将在创建模型时被调用。
控制器是GEF中最复杂的一部分,GEF把控制器完成的工作又分成了几个部分,包括请求和编辑策略及引申出来的命令模式,如图1所示。
图1 控制器
用户的编辑操作被转换为一系列请求(Request),Eclipse中有很多种类的请求,这些种类在GEF里被称为角色(Role)。在GEF里有图形化和非图形化这两大类角色,前者如“Layout Role”对应和布局有关的操作,后者如“Connection Role”对应和连接有关的操作等。角色这个概念是通过编辑策略(EditPolicy)来实现的,EditPolicy的主要功能是根据请求创建相应的命令(Command),而后者会直接操作模型对象。
对每一个EditPart,用户都可以“安装”一些EditPolicy。用户对这个EditPart的特定操作会被交给已安装的对应EditPolicy处理。这样做的直接好处是可以在不同EditPart之间共享一些重复操作。
控制器的实现
每一个模型都会对应一个控制器,控制器主要负责模型和视图的同步。控制器要监听模型的属性改变的事件,并通知视图更新。另外,控制器还要设置相应的编辑策略,用来处理由视图接收的请求。一个简单的控制器代码如例程1所示。
例程1 NodePart.java
public class NodePart extends AbstractGraphicalEditPart implements
PropertyChangeListener, NodeEditPart {
protected DirectEditManager manager;
//处理请求
public void performRequest(Request req) {
if (req.getType().equals(RequestConstants.REQ_DIRECT_EDIT)) {
if (manager == null) {
NodeFigure figure = (NodeFigure) getFigure();
manager = new NodeDirectEditManager(this,
TextCellEditor.class, new NodeCellEditorLocator(figure));
}
manager.show();
}
}
//监听模型属性改变
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Node.PROP_LOCATION))
refreshVisuals();
else if (evt.getPropertyName().equals(Node.PROP_NAME))
refreshVisuals();
else if (evt.getPropertyName().equals(Node.PROP_INPUTS))
refreshTargetConnections();
else if (evt.getPropertyName().equals(Node.PROP_OUTPUTS))
refreshSourceConnections();
}
//创建模型对应的视图
protected IFigure createFigure() {
return new NodeFigure();
}
//设置编辑策略
protected void createEditPolicies() {
installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new NodeDirectEditPolicy());
installEditPolicy(EditPolicy.COMPONENT_ROLE, new NodeEditPolicy());
installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new
NodeGraphicalNodeEditPolicy());
}
//注册自己成为模型的监听器
public void activate() {
if (isActive()) {
return;
}
super.activate();
((Node) getModel()).addPropertyChangeListener(this);
}
//把自己从模型的监听器中删除
public void deactivate() {
if (!isActive()) {
return;
}
super.deactivate();
((Node) getModel()).removePropertyChangeListener(this);
}
//更新视图的显示
protected void refreshVisuals() {
Node node = (Node) getModel();
Point loc = node.getLocation();
Dimension size = new Dimension(150, 40);
Rectangle rectangle = new Rectangle(loc, size);
((NodeFigure) this.getFigure()).setName(((Node) this.getModel()).getName());
((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(),
rectangle);
}
//得到源连接的连接点
public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection) {
return new ChopboxAnchor(getFigure());
}
public ConnectionAnchor getSourceConnectionAnchor(Request request) {
return new ChopboxAnchor(getFigure());
}
//得到目标连接的连接点
public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection) {
return new ChopboxAnchor(getFigure());
}
public ConnectionAnchor getTargetConnectionAnchor(Request request) {
return new ChopboxAnchor(getFigure());
}
//得到以模型作为源的连接列表
protected List getModelSourceConnections() {
return ((Node) this.getModel()).getOutgoingConnections();
}
//得到以模型作为目标的连接列表
protected List getModelTargetConnections() {
return ((Node) this.getModel()).getIncomingConnections();
}
}
上例通过继承AbstractGraphicalEditPart类实现了一个EditPart,另外,当GEF框架创建一个模型时,都会为此模型创建一个对应的控制器。
创建控制器
创建控制器的操作是在控制器工厂中完成的,用户可以在编辑器初始化时指定控制器工厂,例如“getGraphicalViewer().setEditPartFactory (new PartFactory());”,其中PartFactory为控制器工厂,代码如例程2所示。
例程2 PartFactory.java
public class PartFactory implements EditPartFactory {
public EditPart createEditPart(EditPart context, Object model) {
EditPart part = null;
//根据模型创建相应的EditPart
if (model instanceof Diagram)
part = new DiagramPart();
else if (model instanceof Connection)
part = new ConnectionPart();
else
part = new NodePart();
//设置EditPart对应的模型
part.setModel(model);
//返回根据模型创建的EditPart
return part;
}
}
通过创建控制器和控制器工厂,GEF编辑器将会知道在模型创建后,自动调用控制器工厂创建对应的控制器,从而实现模型和控制器的一一对应关系。
请求和编辑策略
请求和编辑策略是GEF框架中减轻控制器的负担、减小代码耦合度而实现的一种解决方案。
请求和编辑策略
请求和编辑策略(Request and EditPolicies)对初学者来说是比较难理解的部分,但正是因为这种机制才使得GEF框架功能强大,而且非常灵活。想象一下,如果用户所有操作的响应都由EditPart来处理会怎样,如果这样的话,EditPart的代码将会“又臭又长”,这是让人无法忍受的。
在EditPart中,可以通过设置不同的编辑策略(EditPolicies)来处理不同的请求,这样,一方面,可以把代码从EditPart中解放出来,分别由不同的EditPolicies进行处理,另一方面,用户可以着力于自己的关注点,但由此也增加读者学习GEF框架的时间。
另外,在EditPart中设置编辑策略时,要指定相应的角色(Role),角色只是一个标识,在同一个EditPart中不能存在两个相同角色的编辑策略,读者可以在GEF的联机文档中找到详细的编辑策略、请求和角色说明。
编辑策略的实现
控制器中通过createEditPolicies方法添加编辑策略,每种编辑策略负责处理相应的请求。通常请求一般会对模型进行操作,在EditPolicies中,可以通过命令的方式操作模型,命令将在后面介绍。EditPolicies的示例代码如例程3所示。
例程3 DiagramLayoutEditPolicy.java
public class DiagramLayoutEditPolicy extends XYLayoutEditPolicy {
protected Command createAddCommand(EditPart child, Object constraint) {
return null;
}
//创建模型位置改变的命令
protected Command createChangeConstraintCommand(EditPart child, Object constraint) {
//如果位置改变的不是Node则返回
if (!(child instanceof NodePart))
return null;
if (!(constraint instanceof Rectangle))
return null;
MoveNodeCommand cmd = new MoveNodeCommand();
cmd.setNode((Node) child.getModel());
//设置模型新的位置信息
cmd.setLocation(((Rectangle) constraint).getLocation());
return cmd;
}
//获得创建模型的命令
protected Command getCreateCommand(CreateRequest request) {
//判断请求创建的是否为Node
if (request.getNewObject() instanceof Node) {
//新建CreateNodeCommand
CreateNodeCommand cmd = new CreateNodeCommand();
//设置父模型
cmd.setDiagram((Diagram) getHost().getModel());
//设置当前模型
cmd.setNode((Node) request.getNewObject());
Rectangle constraint = (Rectangle) getConstraintFor(request);
//设置模型的位置信息
cmd.setLocation(constraint.getLocation());
//返回Command对象
return cmd;
}
return null;
}
protected Command getDeleteDependantCommand(Request request) {
return null;
}
}
通过实现此编辑策略,GEF编辑器将能够处理XYLayoutEditPolicy所能响应的相关请求,并交由相应的Command进行处理。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/13081368/viewspace-402572/ ,如需转载,请注明出处,否则将追究法律责任。