Pamiątka (ang. Memento) to wzorzec z grupy wzorców behawioralnych. Jego głównym zadaniem jest zapisywanie i odczytywanie stanu obiektu. Korzystamy z niego gdy nie chcemy dać użytkownikowi w głównym obiekcie możliwości manipulacji polami czy metodami.

Za przykład może posłużyć aplikacja do edycji tekstu, w której chcemy aby użytkownik miał możliwość cofnięcia się do poprzedniego zapisanego stanu. Tworzymy więc klasę główną będącą naszym orignatorem – „TextEditorApp”. Klasa ta posiada pola prywatne określające na przykład wprowadzony tekst, aktualną czcionkę i styl. Posiada także metody publiczne, dostępne dla użytkownika: do dodawania nowego tekstu/operacji, do zapisywania stanu dokumentu i do ładowania zapisanego stanu. Następnie stwórzmy klasę memento – „TextEditorAppMemento”, która będzie posiadała prywatne pola, konstruktor i gettery dla pól o domyślnym dostępie. Kolejnym krokiem jest stworzenie caretakera – „TextEditorAppCaretaker”, który zawiera prywatną listę obiektów typu TextEditorAppMemento oraz dwie metody publiczne – do dodawania pamiątek, która zapisuje aktualną wersję pliku oraz metoda do pobierania pamiątek, która zwraca ostatnio zapisaną pamiątkę, a więc obiekt TextEditorAppMemento.

Diagram UML wzorca memento

TextEditorApp.java

package memento.example.textEditorApp;


public class TextEditorApp {
    private String text;
    private String currentFont;
    private String styles;

    public void addOperation(String text, String currentFont, String styles) {
        this.text = text;
        this.currentFont = currentFont;
        this.styles = styles;
        System.out.println("Dodano nową operację.");
    }

    public TextEditorAppMemento save() {
        return new TextEditorAppMemento(this.text,this.currentFont, this.styles);
    }

    public void load(TextEditorAppMemento textEditorAppMemento) {
        this.text = textEditorAppMemento.getText();
        this.currentFont = textEditorAppMemento.getCurrentFont();
        this.styles = textEditorAppMemento.getStyles();
    }

}

TextEditorAppMemento.java

package memento.example.textEditorApp;

class TextEditorAppMemento {
    private String text;
    private String currentFont;
    private String styles;

    TextEditorAppMemento(String text, String currentFont, String styles) {
        this.text = text;
        this.currentFont = currentFont;
        this.styles = styles;
    }

    String getText() {
        return text;
    }

    String getCurrentFont() {
        return currentFont;
    }

    String getStyles() {
        return styles;
    }
}

TextEditorAppCaretaker.java

package memento.example.textEditorApp;

import java.util.ArrayList;
import java.util.List;

public class TextEditorAppCaretaker {
    List<TextEditorAppMemento> mementos = new ArrayList<TextEditorAppMemento>();
    public void addMemento(TextEditorAppMemento textEditorAppMemento) {
        mementos.add(textEditorAppMemento);
        System.out.println("Zapisano wersję pliku");
    }

    public TextEditorAppMemento getMemento() {
        int index = mementos.size() - 1;
        System.out.println("Cofnięto operacje do ostatniego zapisu, tekst: " + mementos.get(index).getText());
        return mementos.get(index);
    }
}

Main.java

package memento.example;


import memento.example.textEditorApp.TextEditorApp;
import memento.example.textEditorApp.TextEditorAppCaretaker;

public class Main {

    public static void main(String[] args) {
        TextEditorAppCaretaker textEditorAppCaretaker = new TextEditorAppCaretaker();
        TextEditorApp textEditorApp = new TextEditorApp();

        textEditorApp.addOperation("Strona", "tahoma", "nagłówek 1");
        textEditorApp.addOperation("Strona domowa", "tahoma", "nagłówek 1");

        textEditorAppCaretaker.addMemento(textEditorApp.save());

        textEditorApp.addOperation("Strona domowa bloga", "tahoma", "nagłowek 1");
        textEditorApp.addOperation("Strona domowa bloga programistycznego", "tahoma", "nagłówek 1");

        textEditorApp.load(textEditorAppCaretaker.getMemento());
    }

}

PODSUMOWANIE

Wzorzec pamiątka – memento jest wykorzystywany kiedy, jak już wyżej wspomniano, musimy zapamiętać jeden lub więcej stanów danego obiektu. Wówczas wydzielamy stan obiektu do osobnej klasy – memento, a sterowanie procesem zapisu i odczytu powierzamy klasie trzeciej – caretakerowi. Zaletą wzorca jest enkapsulacja poszczególnych stanów obiektu –  nikt poza originatorem nie ma bezpośredniego dostępu do pamiątek, caretaker zaś przechowuje kolekcję pamiątek, ale nie może modyfikować żadnej z nich. Wzorzec ma wiele zastosowań, jednym z nich są formularze wielokrokowe, kiedy chcemy dać użytkownikowi możliwość cofnięcia się do poprzednich kroków.