Happy new Year everybody!
I’d like to begin 2010 with a design pattern argument.
First design pattern of this year is Memento pattern, that could be very useful if you are working on an AIR desktop application or in a Flex RIA and you want to make an history of user’s actions.
In fact this pattern is used to make undo and redo in an application.
Memento pattern is tightly coupled with originator object and it’s usually composed by 3 different parts:
- memento
- caretaker
- originator
Originator is object that pass data to caretaker and it saved them in a couple of arrays (undoArray and redoArray).
So caretaker is an object that stores a memento snapshot and, if originator needs, pass undo or redo data to this object.
I prepare a sample to understand better what could you use Memento pattern in your applications (“view source” option is activated).
I wrote a Memento class with a Singleton to centralize all users actions,then I create a MementoVO that is a dynamic class to help me store data from each object used by the user, you could use a simple Object instead of a dynamic class to save more memory if you prefer.
Sample is very easy but useful for Memento pattern purpose, in fact you can move windows trough the stage and our “history” class (Memento.as) saves their positions when saveAction method is called by the originator (an instance of CustomWin).
Memento class has 4 essentials methods that are:
- undo()
- redo()
- saveAction(_obj:Object)
- saveRedoAction(_obj:Object)
I start from the end, saveRedoAction, last method, is used to save the position of a window before I restore its positions, usually Redo action is used only one time, but if you want you could create an Array and work with multiple Redo.
Another important method is saveAction that is called when mouse down event was triggered and it stores in memento class own old position and object itselfs.
First two methods need to undo and redo an user action, it’s easy isn’t it?!
I think that in this year I’ll share lots of those patterns with you to share our knowledge together so feel free to add comment at this post and we could make an interesting and useful conversation, I’m totally sure!
Nice Example for Pattern implementation.
I think it’s great that you’re sharing examples like this with the world and encourage you to continue! However, if you’ll permit me, I feel the need to point out that the implementation you described does not match that formal definition of the Memento pattern. For example, the Memento pattern dictates that the Memento object (MomentoVO in your example) should expose no public interface to the Caretaker. In other words, the Caretaker should not be able to read or write any Memento properties. Also, the Originator should be responsible for constructing, populating, and returning new Memento instances to the Caretaker, typically through a “getState()” method (hence the name “Originator”). There are a few other things slightly odd about your implementation. Consider reviewing the formal pattern in the Gang of Four book or on Wikipedia. Also look into the Command pattern which is often used to implement undo/redo functionality and which your implementation has some similarity to.
Cheers!
Hi Kristopher,
I’m totally agree with you and is a good point your explanation, I really appreciate a lot.
I know what are you saying, I’m starting with this sample and now I’m working on same sample
that runs with PureMVC too. So when will be ready I’ll share that one too and this time I’ll write in more formal way.
Thank you again for your contribute
In your application, when you undo and then redo, the last state doesn’t seem to be calculated. For example, move an object twice. Then undo twice. Now redo twice. Only one redo operation will occur…
yes sure! I say in my post that redo is one shoot only…
I appreciate your help.
Thank you very much for this. It was extremely helpful.
hi, i tried use your idea in my project, i have some problem..
code in my component(originator)
..
class MyComp extends Group
addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
var obj:Object = new Object();
obj.who = this;
obj.x = x;
obj.y = y;
obj.rotation = rotation;
obj.alpha = alpha;
memento.saveAction(obj);
});
in another class
public function undo():void {
var tMem:MementoVO = Memento.getInstance().undo();
if(null != tMem) {
var myComp:MyComp = tMem.who as MyComp;
Alert.show(getQualifiedClassName(tMem.who as MyComp));
//this Alert will show ‘null’
Alert.show(getQualifiedClassName(tMem.who));
//this Alert will show ‘global’
}
}
that results is not right, and i can`t use for example myComp.x or myComp.y properties.
thanks!
that code is works ok,
addEventListener(MouseEvent.MOUSE_DOWN, func);
..
private function func(e:Event):void {
//in this function ‘this’ is visible
}
in case that code
addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
// ‘this’ is not visible, only ‘global’
});
thanks