Memento programtervezési minta

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.

UML ábra

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.

import java.util.List;
import java.util.ArrayList;
class Originator {
    private String state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento..
 
    public void set(String state) {
        System.out.println("Originator: Setting state to " + state);
        this.state = state;
    }
 
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(this.state);
    }
 
    public void restoreFromMemento(Memento memento) {
        this.state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }
 
    public class Memento {
        private final String state;
 
        public Memento(String stateToSave) {
            state = stateToSave;
        }
 
        public String getSavedState() {
            return state;
        }
    }
}
 
class Caretaker {
    public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
 
        Originator originator = new Originator();
        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:

  1. Szerializáció.
  2. Egy osztályt deklarálunk ugyanabban a csomagban.
  3. 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 objektum
    public class OriginalObject
    {
        public string String1 { get; set; }
        public string String2 { get; set; }
        public Memento MyMemento { get; set; }

        public OriginalObject(string str1, string str2)
        {
            this.String1 = str1;
            this.String2 = str2;
            this.MyMemento = new Memento(str1, str2);
        }
        public void Revert()
        {
            this.String1 = this.MyMemento.string1;
            this.String2 = this.MyMemento.string2;
        }
    }
    
    //memento objektum
    public class Memento
    {
        public readonly string string1;
        public readonly string string2;

        public Memento(string str1, string str2)
        {
            string1 = str1;
            string2 = str2;
        }
    }

Jegyzetek

Források

További információk

Kapcsolódó szócikkek