A Memento programtervezési minta biztosítja, hogy egy objektum visszaállítható legyen az előző állapotába.
A Memento minta három objektummal implementálható: originator (kezdeményező), caretaker (gondnok), memento (emlékeztető). Az originator egy objektum, aminek van valamilyen belső állapota. A caretaker valamit módosít az originator belső állapotán, azonban azt akarja, hogy lehetősége legyen visszavonni a változásokat. A caretaker először kér az originator-tól egy memento objektumot. Ezután valamilyen műveletet (vagy műveleteket) végez az originator-on, majd, ha vissza akarja állítani az előző állapotot, akkor visszaadja az originator-nak a memento objektumot. A memento objektum önmagában egy átlátszatlan objektum (a caretaker nem változtathatja meg, vagy nem ajánlott megváltoztatnia). Amikor ezt a mintát használjuk, ügyelni kell arra, hogy ez csak egy objektummal foglalkozik, az originator eközben megváltoztathat más objektumokat vagy erőforrásokat.
Különösen jól használható például visszavonás (undo/redo) funkcionalitás megvalósításakor dokumentumszerkesztés során. Előnye, hogy nem sérti az egységbezárás határait. Hátránya, hogy sokszor erőforrásigényes (pl. teljes dokumentumtartalom mentése, több példányban).
Java példa
A következő Java program illusztrálja a Memento minta "visszavonás"ának használatát.
importjava.util.List;importjava.util.ArrayList;classOriginator{privateStringstate;// The class could also contain additional data that is not part of the// state saved in the memento..publicvoidset(Stringstate){System.out.println("Originator: Setting state to "+state);this.state=state;}publicMementosaveToMemento(){System.out.println("Originator: Saving to Memento.");returnnewMemento(this.state);}publicvoidrestoreFromMemento(Mementomemento){this.state=memento.getSavedState();System.out.println("Originator: State after restoring from Memento: "+state);}publicclassMemento{privatefinalStringstate;publicMemento(StringstateToSave){state=stateToSave;}publicStringgetSavedState(){returnstate;}}}classCaretaker{publicstaticvoidmain(String[]args){List<Originator.Memento>savedStates=newArrayList<Originator.Memento>();Originatororiginator=newOriginator();originator.set("State1");originator.set("State2");savedStates.add(originator.saveToMemento());originator.set("State3");// We can request multiple mementos, and choose which one to roll back to.savedStates.add(originator.saveToMemento());originator.set("State4");originator.restoreFromMemento(savedStates.get(1));}}
A program kimenete:
Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3
Ez a példa egy Stringet használ állapotként, amelyik egy megváltoztathatatlan objektum Java-ban.
Valós életbeli forgatókönyvek esetén az állapot csaknem minden esetben egy objektum lesz és ez esetben az állapot egy példányát kell használni.
Megjegyezzük, hogy megvalósítás rámutat egy hátrányra is: deklarálni kell egy belső osztályt. Jobb lenne, ha a mementó stratégiát egynél több objektumra lehetne alkalmazni.
Leginkább három módja van a mementó elérésének:
Szerializáció.
Egy osztályt deklarálunk ugyanabban a csomagban.
Az objektum egy proxyn keresztül szintén elérhetővé tehető, az adott objektumon egy save/restore (mentés/visszatöltés) műveletén keresztül.
C# Példa
A Memento minta lehetővé teszi, hogy rögzítsük egy objektum állapotát az egységbezárás megsértése nélkül, úgy, hogy később vissza tudjuk azt állítani. Itt egy olyan példa látható ahol a memento objektumot konkrétan arra használjuk, hogy visszavonjuk az objektumban történő változásokat:
//IVSR : Memento példa C# kód//eredeti objektumpublicclassOriginalObject{publicstringString1{get;set;}publicstringString2{get;set;}publicMementoMyMemento{get;set;}publicOriginalObject(stringstr1,stringstr2){this.String1=str1;this.String2=str2;this.MyMemento=newMemento(str1,str2);}publicvoidRevert(){this.String1=this.MyMemento.string1;this.String2=this.MyMemento.string2;}}//memento objektumpublicclassMemento{publicreadonlystringstring1;publicreadonlystringstring2;publicMemento(stringstr1,stringstr2){string1=str1;string2=str2;}}