我正在开发几个月前创建的Java中的一个小型UML编辑器项目。几周后,我得到了一个UML类图编辑器的工作副本。但现在,我正在重新设计它以支持其他类型的图表,例如序列,状态,类等等。这是通过实现图形构建框架完成的(我受到了Cay Horstmann的大力启发使用Violet UML编辑器的主题)。用纪念图案(和命令)存储复杂对象的状态
重新设计工作进展顺利,直到我的一位朋友告诉我忘记为项目添加撤销/撤消功能,而在我看来,这一功能至关重要。
记住面向对象的设计课程,我立即想到了Memento和Command模式。
这是交易。我有一个抽象类AbstractDiagram,它包含两个ArrayList:一个用于存储节点(在我的项目中称为元素),另一个用于存储边(在我的项目中称为链接)。该图可能会保留一堆可以撤销/重做的命令。相当标准。
如何以有效的方式执行这些命令?举例来说,我想移动一个节点(该节点将是一个名为INode的接口类型,并且会从它派生出具体的节点(ClassNode,InterfaceNode,NoteNode等))。
位置信息作为一个属性保存在节点中,所以通过在节点本身中实现该属性来改变状态。当显示刷新时,节点将移动。这是该模式的Memento部分(我认为),区别在于对象是状态本身。此外,如果我保留原始节点的克隆(移动之前),我可以回到它的旧版本。同样的技术适用于节点中包含的信息(类或接口名称,备注节点的文本,属性名称等)。
问题是,在撤销/重做操作时,如何在图中用克隆代替节点?如果我克隆图中引用的原始对象(位于节点列表中),则克隆不在图中引用,唯一指向的是命令本身!首先,我在图表中包含根据ID查找节点的机制(例如),以便我可以在图中用克隆(反之亦然)替换节点?这是否由纪念品和命令模式来做到这一点?链接怎么样?它们也应该是可移动的,但我不想仅为链接创建一个命令(并且仅针对节点),并且我应该能够根据命令对象的类型修改正确的列表(节点或链接)指的是。
你将如何进行?简而言之,我无法在命令/备忘录模式中表示对象的状态,以便可以高效地恢复对象,并在图表列表中恢复原始对象,并根据对象类型(节点或链接)进行恢复。
非常感谢!
Guillaume。
P.S .:如果我不清楚,告诉我,我会澄清我的消息(一如既往!)。
编辑
这里是我的实际解决方案,我开始张贴了这个问题之前实施。
首先,我必须定义如下的AbstractCommand类:
public abstract class AbstractCommand {
public boolean blnComplete;
public void setComplete(boolean complete) {
this.blnComplete = complete;
}
public boolean isComplete() {
return this.blnComplete;
}
public abstract void execute();
public abstract void unexecute();
}
然后,将各类型的命令是利用AbstractCommand的具体推导实现。
所以我有一个命令来移动的对象:
public class MoveCommand extends AbstractCommand {
Moveable movingObject;
Point2D startPos;
Point2D endPos;
public MoveCommand(Point2D start) {
this.startPos = start;
}
public void execute() {
if(this.movingObject != null && this.endPos != null)
this.movingObject.moveTo(this.endPos);
}
public void unexecute() {
if(this.movingObject != null && this.startPos != null)
this.movingObject.moveTo(this.startPos);
}
public void setStart(Point2D start) {
this.startPos = start;
}
public void setEnd(Point2D end) {
this.endPos = end;
}
}
我也有一个MoveRemoveCommand(为...移动或删除对象/节点)。如果我使用instanceof方法的ID,我不必将图传递给实际节点或链接,以便它可以从图中移除(这是我认为的一个糟糕的主意)。
AbstractDiagram diagram; Addable obj; AddRemoveType类型;
@SuppressWarnings("unused")
private AddRemoveCommand() {}
public AddRemoveCommand(AbstractDiagram diagram, Addable obj, AddRemoveType type) {
this.diagram = diagram;
this.obj = obj;
this.type = type;
}
public void execute() {
if(obj != null && diagram != null) {
switch(type) {
case ADD:
this.obj.addToDiagram(diagram);
break;
case REMOVE:
this.obj.removeFromDiagram(diagram);
break;
}
}
}
public void unexecute() {
if(obj != null && diagram != null) {
switch(type) {
case ADD:
this.obj.removeFromDiagram(diagram);
break;
case REMOVE:
this.obj.addToDiagram(diagram);
break;
}
}
}
最后,我有一个ModificationCommand用于修改节点或链接(类名等)的信息。这可能会在未来与MoveCommand合并。现在这个类是空的。我可能会使用一种机制来确定修改后的对象是节点还是边缘(通过instanceof或ID中的特殊含义)。
这是一个很好的解决方案吗?