Pular para o conteúdo principal

Memento

Descrição​

O Memento captura e externaliza o estado interno de um objeto, sem violar seu encapsulamento, para que esse objeto possa ser restaurado a esse estado mais tarde. É o padrão clássico por trás de funcionalidades de desfazer (undo), checkpoints e rollback.

O padrão envolve três participantes. O Originator é o objeto cujo estado se quer salvar: ele sabe criar um memento com um snapshot do próprio estado e restaurar-se a partir de um memento. O Memento é o objeto que armazena esse snapshot, normalmente imutável. O Caretaker guarda os mementos (por exemplo, em uma pilha de histórico) e decide quando salvar e quando restaurar, mas nunca lê nem altera o conteúdo deles.

O ponto central é o encapsulamento: o Memento expõe uma interface "estreita" para o Caretaker (que só o armazena e o repassa) e uma interface "larga" apenas para o Originator (que acessa o estado). Em linguagens sem friend ou visibilidade de pacote, isso costuma ser obtido com classes aninhadas, interfaces opacas ou objetos imutáveis.

Problemas que resolve​

  • Implementar desfazer/refazer sem expor os campos internos do objeto a outras classes
  • Criar checkpoints para reverter um objeto após uma operação que falhou ou foi cancelada
  • Evitar que código externo (histórico, gerenciador de transações) dependa da estrutura interna do objeto salvo
  • Guardar versões anteriores de um estado para comparação ou navegação (histórico de edições)

Quando usar​

  • É necessário salvar snapshots do estado de um objeto para restaurá-lo depois
  • O acesso direto aos campos do objeto quebraria o encapsulamento ou exporia detalhes de implementação
  • Editores, formulários, jogos (save/load) ou wizards precisam de undo, histórico ou rollback local
  • Operações transacionais em memória que podem precisar ser revertidas

Quando evitar​

  • O estado é grande e os snapshots são frequentes, gerando alto consumo de memória — prefira armazenar diferenças ou usar Command com operações inversas
  • O objeto é simples e totalmente público; uma cópia direta ou um Prototype resolve
  • A persistência de longo prazo é o objetivo — serialização, event sourcing ou versionamento em banco são mais adequados

Trade-offs​

GanhoCusto / Risco
Preserva o encapsulamento do OriginatorSnapshots completos podem consumir muita memória
Simplifica o Originator, delegando o histórico ao CaretakerO Caretaker precisa gerenciar o ciclo de vida dos mementos (limites, descarte)
Facilita undo/redo e rollbackEm linguagens dinâmicas é difícil garantir que só o Originator acesse o estado
Mementos imutáveis são seguros para compartilharCópia rasa de estado mutável pode causar restaurações incorretas
  • Acoplamento: o Caretaker depende apenas do tipo opaco do memento, não do estado interno.
  • Desempenho: considere snapshots incrementais, compressão ou limite de histórico para estados grandes.
  • Testabilidade: salvar e restaurar é facilmente verificável comparando o estado antes e depois.
  • Consistência: garanta cópias profundas de coleções e objetos mutáveis dentro do memento.

Diagrama​

O Originator cria mementos com seu estado e os usa para se restaurar; o Caretaker mantém o histórico de mementos e aciona salvar/restaurar no Originator sem acessar o conteúdo dos snapshots.

Exemplo​

Um editor de texto permite desfazer edições: antes de cada alteração, o histórico (Caretaker) pede ao editor (Originator) um snapshot do conteúdo e da posição do cursor.

class EditorMemento {
constructor(readonly content: string, readonly cursor: number) {}
}

class Editor {
private content = "";
private cursor = 0;

type(text: string) {
this.content += text;
this.cursor = this.content.length;
}
save(): EditorMemento {
return new EditorMemento(this.content, this.cursor);
}
restore(m: EditorMemento) {
this.content = m.content;
this.cursor = m.cursor;
}
}

class History {
private stack: EditorMemento[] = [];
constructor(private editor: Editor) {}

backup() { this.stack.push(this.editor.save()); }
undo() {
const m = this.stack.pop();
if (m) this.editor.restore(m);
}
}

Padrões relacionados​

  • Command — comandos podem usar mementos para guardar o estado necessário ao undo()
  • Iterator — um memento pode registrar o estado de uma iteração para retomá-la depois
  • Prototype — clonar o objeto é uma alternativa mais simples ao memento quando o estado é público e direto
  • State — mementos podem salvar e restaurar o estado corrente de uma máquina de estados

Referência​