Memento Pattern – salvarea și restaurarea stării obiectelor
În aplicațiile reale apar frecvent cerințe precum Undo / Redo, istoric al modificărilor, revenire la o stare anterioară sau snapshot-uri de stare.
Problema apare atunci când vrem să salvăm starea unui obiect fără să-i expunem structura internă.
Aici intervine Memento Pattern, un behavioral design pattern care ne ajută să:
-
salvăm starea unui obiect,
-
restaurăm acea stare ulterior,
-
fără a încălca principiul encapsulation.
Problema pe care o rezolvă Memento Pattern
Să presupunem că avem un obiect complex (ex: document, formular, editor de text):
-
are multe proprietăți interne;
-
starea se modifică frecvent;
-
avem nevoie de undo / redo.
❌ Soluții greșite:
-
expunerea proprietăților interne;
-
copierea obiectului complet;
-
logica de undo împrăștiată peste tot în cod.
✅ Soluția corectă:
Obiectul își creează singur un „snapshot” al stării → Memento.
Structura pattern-ului
Memento Pattern implică trei roluri clare:
1️⃣ Originator
-
obiectul a cărui stare trebuie salvată;
-
creează și restaurează memento-uri.
2️⃣ Memento
-
conține starea internă;
-
este opac pentru restul aplicației.
3️⃣ Caretaker
-
gestionează lista de memento-uri;
-
nu cunoaște conținutul acestora.
Diagramă conceptuală (simplificată)
Caretaker ---> Memento <--- Originator
^
|
creează / restaurează
Exemplu practic în C# – Undo pentru un document
🎯 Scenariul
Avem un editor de documente care:
-
modifică textul;
-
permite undo.
1️⃣ Memento – snapshot-ul stării
public class DocumentMemento
{
public string Content { get; }
public DocumentMemento(string content)
{
Content = content;
}
}
🔒 Observație:
Memento doar stochează starea, nu conține logică.
2️⃣ Originator – obiectul principal
public class Document
{
public string Content { get; private set; } = string.Empty;
public void Write(string text)
{
Content += text;
}
public DocumentMemento Save()
{
return new DocumentMemento(Content);
}
public void Restore(DocumentMemento memento)
{
Content = memento.Content;
}
}
📌 Documentul:
-
își creează singur snapshot-ul;
-
știe cum să-și restaureze starea.
3️⃣ Caretaker – managerul de undo
public class DocumentHistory
{
private readonly Stack<DocumentMemento> _history = new();
public void Save(DocumentMemento memento)
{
_history.Push(memento);
}
public DocumentMemento? Undo()
{
return _history.Count > 0 ? _history.Pop() : null;
}
}
🔐 Caretaker-ul:
-
nu știe ce conține memento-ul;
-
doar îl stochează și îl returnează.
4️⃣ Utilizare
var document = new Document();
var history = new DocumentHistory();
document.Write("Hello ");
history.Save(document.Save());
document.Write("World!");
history.Save(document.Save());
Console.WriteLine(document.Content); // Hello World!
var previousState = history.Undo();
if (previousState != null)
{
document.Restore(previousState);
}
Console.WriteLine(document.Content); // Hello
Beneficii
✅ Menține encapsulation
✅ Cod curat pentru undo/redo
✅ Separare clară a responsabilităților
✅ Ușor de extins (multi-level undo, redo stack)
Dezavantaje
⚠️ Consum de memorie (multe snapshot-uri)
⚠️ Necesită atenție la obiecte foarte mari
⚠️ Poate deveni costisitor fără limitare de istoric
Când să folosești Memento Pattern
✔ Undo / Redo
✔ Istoric modificări
✔ Snapshot-uri de stare
✔ Editoare, formulare, configurări
✔ Aplicații desktop, web, enterprise
❌ Evită dacă:
-
starea este trivială;
-
nu ai nevoie de restaurare;
-
obiectele sunt foarte mari și mutate frecvent.
Memento Pattern vs alte pattern-uri
|
Pattern |
Scop |
|---|---|
|
Memento |
Salvarea stării |
|
Command |
Undo prin inversarea acțiunilor |
|
Prototype |
Copiere obiect |
|
State |
Comportament în funcție de stare |
📌 Memento ≠ Command, dar se folosesc frecvent împreună.
Concluzie
Memento Pattern este soluția elegantă pentru gestionarea stării fără compromisuri arhitecturale.
Este indispensabil atunci când vrei funcționalități de tip undo/redo fără a transforma codul într-un „monstru” greu de întreținut.