Saving the state of a complex object using the Memento (and Command) template

I am working on a small UML editor project in Java that I started a couple of months ago. A few weeks later I received a working copy of the UML class diagram editor.

But now I am completely redesigning it to support other types of diagrams, such as sequence, state, class, etc. This is done by implementing the graph construction structure (I am very inspired by the work of Kay Horstman on using the Violet UML editor).

The redesign went smoothly until one of my friends told me that I forgot to add Do / Undo functionality to the project, which, in my opinion, is vital.

Remembering the object-oriented design courses, I immediately thought of the Memento and Command patterns.

Here's the deal. I have an AbstractDiagram abstract class containing two ArrayLists: one for storing nodes (called Elements in my project), and the other for storing Edges (called links in my projects). The chart is likely to contain a stack of commands that can be undone / undone. Pretty standard.

How can I effectively execute these commands? Say, for example, that I want to move a node (node ​​will be an interface type named INode, and specific nodes (ClassNode, InterfaceNode, NoteNode, etc.) will be obtained from it).

Location information is stored as an attribute in the node, so by changing this attribute in the node itself, the state changes. When the display is updated, the node will be moved. This is part of the Memento template (I think), with the difference that the object is the state itself.

Also, if I save a clone of the original node (before moving it), I can revert to its old version. The same method is used for information contained in a node (class or interface name, node text, node name, attribute name, etc.).

The thing is, how to replace the node with your clone on the undo / redo operation in the diagram? If I clone the source object referenced by the diagram (being in the node list), the clone is not a link to the diagram, and the only thing that indicates the command itself! Shoud do I enable the mechanisms on the diagram to find the node according to the identifier (for example), so can I replace the node on the diagram with my clone (and vice versa)? Do you need Memento and Command templates for this? What about links? They must also be movable, but I do not want to create a command only for links (and only for nodes), and I should be able to change the correct list (nodes or links) according to the type of object. The command refers to.

What will you do? In short, I had a problem with representing the state of the object in the command / memento template so that it can be restored efficiently and the original object is restored in the diagram list depending on the type of object (node ​​or link).

Thanks a lot!

Guillaume.

PS: If I don’t know, tell me and I will clarify my message (as always!).

Edit

Here is my actual solution that I started implementing before posting this question.

Firstly, I have an AbstractCommand class, which is defined as follows:

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(); } 

Then each type of command is implemented using a specific AbstractCommand output.

So, I have a command to move an object:

 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; } } 

I also have MoveRemoveCommand (for ... moving or deleting an object / node). If I use the instanceof method identifier, I don’t need to pass the chart to the actual node or link so that it can be removed from the chart (which I think is a bad idea).

Abstract chart diagram; The added object; Type 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; } } } 

Finally, I have a ModificationCommand, which is used to change the information about a node or link (class name, etc.). It may be combined in the future with MoveCommand. While this class is empty. I will probably make an identifier with a mechanism to determine if the modified object is a node or an edge (via instanceof or a special notation in the ID).

This is a good decision?

+4
source share
2 answers

I think you just need to break your problem down into smaller ones.

First problem: Q: How to present steps in your application using the memento / command template? Firstly, I have no idea how your application works, but I hope you see where I'm going. Say I want to place ClassNode on a chart with the following properties

 { width:100, height:50, position:(10,25), content:"Am I certain?", edge-connections:null} 

This will be completed as a command object. Let's say what goes in the DiagramController. Then the responsibility of the chart manager may be to write this command (click on the stack, this is my bet) and transfer the command to DiagramBuilder, for example. DiagramBuilder is actually responsible for updating the display.

 DiagramController { public DiagramController(diagramBuilder:DiagramBuilder) { this._diagramBuilder = diagramBuilder; this._commandStack = new Stack(); } public void Add(node:ConditionalNode) { this._commandStack.push(node); this._diagramBuilder.Draw(node); } public void Undo() { var node = this._commandStack.pop(); this._diagramBuilderUndraw(node); } } 

Some things like this should do this, and, of course, there will be many details to figure out. By the way, more properties that your nodes have a more detailed Undraw should be.

Using an identifier to associate a command in your stack with an element drawn may be a good idea. It might look like this:

 DiagramController { public DiagramController(diagramBuilder:DiagramBuilder) { this._diagramBuilder = diagramBuilder; this._commandStack = new Stack(); } public void Add(node:ConditionalNode) { string graphicalRefId = this._diagramBuilder.Draw(node); var nodePair = new KeyValuePair<string, ConditionalNode> (graphicalRefId, node); this._commandStack.push(nodePair); } public void Undo() { var nodePair = this._commandStack.pop(); this._diagramBuilderUndraw(nodePair.Key); } } 

At this point, you do not have to have an object, since you have an identifier, but it will be useful if you decide to also implement the replay functions. A good way to generate an identifier for your nodes would be to implement a hash code method for them, except for the fact that you are not guaranteed not to duplicate your nodes in such a way that the hash code is identical.

The next part of the problem is inside your DiagramBuilder, because you are trying to figure out how to hell with these commands. To do this, all I can say is to make sure that you can create the opposite action for each type of component that you can add. To handle the subtraction, you can look at the edge-connection property (the links in the code, which I think), and notify each of the border connections that they should disconnect from a particular node. I would suggest that when disconnected, they can redraw correctly.

To pick up a resume, I would recommend not supporting the link to your nodes on the stack, but instead just a kind of token that represents the given state of the node at that point. This will allow you to represent the same node in your undo stack in multiple places without referring to the same object.

Message if you have Q. This is a tough problem.

+4
source

In my humble opinion, you think about it in a more complex way than it really is. To return to the previous state, a clone of integer nodes is not required at all. Most likely, each Command class will have -

  • the link to node is valid,
  • memento object (having state variables sufficient to return node)
  • execute () method
  • undo ().

Since command classes have a link to a node, we do not need an identifier mechanism to refer to objects in the diagram.

In the example from your question, we want to move the node to a new position. For this, we have the class NodePositionChangeCommand.

 public class NodePositionChangeCommand { // This command will act upon this node private Node node; // Old state is stored here private NodePositionMemento previousNodePosition; NodePositionChangeCommand(Node node) { this.node = node; } public void execute(NodePositionMemento newPosition) { // Save current state in memento object previousNodePosition // Act upon this.node } public void undo() { // Update this.node object with values from this.previousNodePosition } } 

What about links? They should also be movable, but I do not want to create a command only for links (and only for nodes).

I read in the GoF book (in the discussion in memory) that moving a link with changing the position of nodes is handled by some kind of constraint resolver.

+1
source

All Articles