
DESIGN PATTERNS in Java | Memento
Il pattern Memento si utilizza per salvare e ripristinare i diversi stati di un oggetto.
CLASSIFICAZIONE
Il Memento è classificato tra i pattern comportamentali (behavioural).
PROBLEMA E CAMPO DI APPLICAZIONE
Come è noto ogni oggetto (inteso come istanza di una classe) è caratterizzato da uno stato variabile nel tempo, rappresentato dai valori dei suoi attributi.
Tuttavia può essere utile recuperare un precedente stato senza però violare il principio dell'incapsulamento, ovvero accedendo direttamente dall'esterno a tali attributi.
Ad esempio (quasi) tutte le applicazioni dispongono di una funzionalità di Undo (richiamabile attraverso la combinazione dei tasti CTRL+Z) con cui annullare l'ultima modifica effettuata e di fatto ripristinare lo stato precedente.
SOLUZIONE
Il pattern è implementato utilizzando tre differenti classi: Originator, Memento e CareTaker.
La classe Originator contiene lo stato che vogliamo salvare e ripristinare.
Ai classici getter e setter si affiancano due ulteriori metodi per salvare lo stato in un oggetto Memento, creato ex-novo all'occorrenza.
Questo particolare oggetto consentirà di ripristinare un precedente stato aggiornando quello corrente.
Una possibile implementazione è la seguente
public class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
La classe Memento tiene traccia dello stato di un oggetto che in questo caso è rappresentato da una semplice stringa ma che potrebbe essere una classe ben più complessa.
L'obiettivo principale è di esporre il minor numero possibile di dettagli interni della classe, proprio per preservare l'incapsulamento della stessa.
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
Il CareTaker mantiene una lista di Memento e gestisce l'inserimento e il recupero degli elementi, ovvero delle diverse versioni dello stato dell'Originator.
public class CareTaker {
private final List<Memento> mementoList = new ArrayList<>();
public void add(Memento state) {
mementoList.add(state);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
Per simulare l'esatta sequenza di operazioni necessarie al salvataggio e ripristino dello stato ci serviamo di una classe Client.
Prima di tutto abbiamo bisogno di creare un'istanza di Originator e di CareTaker.
N.B. Non dobbiamo creare direttamente delle istanze di Memento perchè questo avverrà in automatico all'occorrenza.
L'Originator imposta il suo stato ("State 1") quindi il CareTaker effettua il salvataggio non direttamente (altrimenti sarebbe violato l'incapsulamento), ma richiamando il metodo pubblico saveStatetoMemento() dell'Originator per poi aggiungere l'oggetto Memento restituito all'interno della propria lista.
Qundi l'Originator modifica ancora il proprio stato e il CareTaker provvede al conseguente salvataggio.
In fase di recupero di un precedente stato il client utilizza il metodo getStateFromMemento() dell'Originator specificando come parametro un indice della lista gestita dal CareTaker.
public class Client {
public static void main(String args[]) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
//Set initial originator state
originator.setState("State 1");
//Save state to careTaker
careTaker.add(originator.saveStateToMemento());
//Change current state
originator.setState("State 2");
//Save to careTaker
careTaker.add(originator.saveStateToMemento());
//Change current state
originator.setState("State 3");
//Save to careTaker
careTaker.add(originator.saveStateToMemento());
System.out.println("Current State is: " + originator.getState());
originator.getStateFromMemento(careTaker.get(0));
System.out.println("Initial state : " + originator.getState());
}
}
Osserviamo che quando viene recuperato uno stato questo non viene cancellato dalla lista. E infatti nelle applicazioni reali è possibile annullare e ripristinare le modifiche con i comandi Undo e Redo.
[LINKS]
`

