Memento
Captura e externaliza o estado interno de um objeto sem violar seu encapsulamento, permitindo restaurá-lo a esse estado posteriormente — a base do mecanismo de undo por snapshot completo.
Intenção
Capturar o estado interno de um objeto num objeto separado (Memento), sem expor os detalhes de implementação desse estado ao mundo externo. O Memento pode ser armazenado e posteriormente usado para restaurar o objeto ao estado capturado — implementando undo, checkpoints, transações e histórico de estados.
Catalogado pelo GoF (1994) como padrão comportamental, o Memento preserva o encapsulamento do Originator: quem armazena o Memento (o Caretaker) não sabe o que há dentro dele — apenas guarda e devolve. Apenas o Originator conhece o formato do estado e sabe como criar e restaurar um Memento. Isso distingue o padrão de uma simples serialização pública do estado: a interface do Memento é opaca para qualquer objeto que não seja o Originator que o criou.
Problema
Continuando o domínio do editor de texto do padrão Command: queremos implementar undo. A abordagem ingênua é expor o estado do editor publicamente para que um objeto externo possa salvá-lo:
// Abordagem ingênua — viola encapsulamento:
class Editor {
conteudo: string = ''; // público para o Caretaker salvar
cursorPos: number = 0; // público para o Caretaker salvar
selecao: [number, number] | null = null; // público
}
// O Caretaker acessa e conhece os internos do Editor — forte acoplamento.
// Se o Editor mudar seus campos, o Caretaker quebra.
const estadoSalvo = {
conteudo: editor.conteudo,
cursorPos: editor.cursorPos,
selecao: editor.selecao,
};
O problema é duplo: o Caretaker (quem armazena o estado) precisa conhecer os detalhes internos do Editor (quem tem o estado), criando acoplamento forte. Qualquer refatoração interna do Editor — renomear um campo, mudar a estrutura da seleção — quebra o Caretaker. O Memento elimina isso: o Editor produz um objeto opaco com seu estado, e o Caretaker apenas o armazena — sem inspecioná-lo.
Distinção crítica: Memento vs Command com undo
Ambos os padrões implementam undo, mas com filosofias diferentes:
-
Command com undo: cada Command encapsula uma ação
específica e sabe como revertê-la incrementalmente. Um
InserirTextoCommandsabe deletar exatamente o trecho que inseriu. O undo é preciso mas exige que cada Command capture o estado suficiente para sua própria reversão. - Memento: captura o estado completo do Originator num snapshot. O undo restaura o estado inteiro — não desfaz uma ação específica, mas retorna o objeto a um ponto anterior no tempo. Mais simples de implementar para objetos com estado rico e interdependente, mas mais custoso em memória (um snapshot por ponto de restauração).
Os dois padrões são complementares: Command pode usar Memento como
mecanismo de captura de estado em seu execute(), delegando
a responsabilidade de "como salvar o estado" ao próprio Originator.
Solução
O Memento organiza o código em três participantes:
-
Originator: o objeto cujo estado queremos capturar.
Cria Mementos via
salvarEstado()e restaura seu estado a partir de um Memento viarestaurarEstado(memento). É o único que conhece o conteúdo interno do Memento. Ex.:Editorcom conteúdo, posição do cursor e seleção. - Memento: objeto que armazena o snapshot do estado do Originator. Sua interface para o Caretaker é opaca — não expõe getters do estado interno. Apenas o Originator acessa o conteúdo. Em TypeScript, isso pode ser modelado com uma classe interna ou com campos privados e um método de acesso restrito via tipo nominal.
-
Caretaker: gerencia o histórico de Mementos. Não
inspeciona nem modifica o conteúdo dos Mementos — apenas os armazena
em ordem e os devolve ao Originator quando o undo é solicitado.
Ex.:
Historicocom pilha de Mementos e métodossalvar()edesfazer().
Estrutura
Editor (Originator)
┌──────────────────────────────────────────────┐
│ - conteudo: string │
│ - cursorPos: number │
│ - selecao: [number,number] | null │
│ │
│ + salvarEstado(): EditorSnapshot │
│ → cria Memento com cópia do estado atual │
│ + restaurarEstado(s: EditorSnapshot): void │
│ → recupera estado do Memento │
│ + (operações de edição) │
└──────────────────────────────────────────────┘
cria e restaura ▲ ▼ armazenado por
│ │
EditorSnapshot (Memento) — interface opaca para o Caretaker
┌──────────────────────────────────────────────┐
│ (campos privados — só o Editor acessa) │
│ - conteudo: string │
│ - cursorPos: number │
│ - selecao: [number,number] | null │
│ │
│ (sem getters públicos — opaco ao Caretaker) │
└──────────────────────────────────────────────┘
▲ guarda e devolve
│
Historico (Caretaker)
┌──────────────────────────────────────────────┐
│ - pilha: EditorSnapshot[] │
│ │
│ + salvar(originator: Editor): void │
│ → pilha.push(editor.salvarEstado()) │
│ + desfazer(originator: Editor): void │
│ → editor.restaurarEstado(pilha.pop()) │
│ │
│ !! Nunca inspeciona o conteúdo do Memento !! │
└──────────────────────────────────────────────┘
Comparação de abordagens para undo:
Command + undo incremental Memento + snapshot
───────────────────────────── ──────────────────────────────
Cada Command conhece seu undo Originator conhece seu estado
Memória: proporcional à ação Memória: proporcional ao estado
Preciso para ações atômicas Simples para estado complexo
Mais boilerplate por tipo de ação Menos código, mais memória
Exemplos de código
Exemplo 1 — Editor de texto com undo por snapshot (Memento)
O mesmo domínio do Command, mas agora o undo captura o estado completo
do editor num snapshot. Note como o Historico (Caretaker)
não acessa nenhum dado interno do EditorSnapshot — apenas
o armazena e o devolve. A comparação com Command fica explícita nos
comentários.
// ── Memento — snapshot opaco do Editor ────────────────────────
// A interface exposta ao Caretaker é opaca: sem getters públicos.
// Somente o Editor acessa os campos via método de pacote (friend pattern).
class EditorSnapshot {
// Campos readonly — imutáveis após a criação do snapshot.
constructor(
private readonly conteudo: string,
private readonly cursorPos: number,
private readonly selecao: readonly [number, number] | null
) {}
// Método restrito: na prática o Editor é o único que o chama.
// Em linguagens com friend/package-private, isso seria inacessível ao Caretaker.
_recuperar(): {
conteudo: string;
cursorPos: number;
selecao: readonly [number, number] | null;
} {
return {
conteudo: this.conteudo,
cursorPos: this.cursorPos,
selecao: this.selecao ? [...this.selecao] : null,
};
}
}
// ── Originator — cria e restaura Mementos ────────────────────
class Editor {
private conteudo: string = '';
private cursorPos: number = 0;
private selecao: [number, number] | null = null;
// Operações de edição
inserir(texto: string): void {
this.conteudo =
this.conteudo.slice(0, this.cursorPos) +
texto +
this.conteudo.slice(this.cursorPos);
this.cursorPos += texto.length;
}
deletarAnterior(): void {
if (this.cursorPos === 0) return;
this.conteudo =
this.conteudo.slice(0, this.cursorPos - 1) +
this.conteudo.slice(this.cursorPos);
this.cursorPos--;
}
moverCursor(pos: number): void {
this.cursorPos = Math.max(0, Math.min(pos, this.conteudo.length));
}
selecionarIntervalo(inicio: number, fim: number): void {
this.selecao = [inicio, fim];
}
// Cria snapshot do estado COMPLETO
salvarEstado(): EditorSnapshot {
return new EditorSnapshot(
this.conteudo,
this.cursorPos,
this.selecao ? [...this.selecao] : null
);
}
// Restaura a partir de um snapshot
restaurarEstado(snapshot: EditorSnapshot): void {
const estado = snapshot._recuperar();
this.conteudo = estado.conteudo;
this.cursorPos = estado.cursorPos;
this.selecao = estado.selecao ? [...estado.selecao] : null;
}
toString(): string {
const sel = this.selecao
? ` [sel ${this.selecao[0]}-${this.selecao[1]}]`
: '';
return `"${this.conteudo}" cursor=${this.cursorPos}${sel}`;
}
}
// ── Caretaker — armazena histórico de snapshots ───────────────
class Historico {
private readonly pilha: EditorSnapshot[] = [];
// Salva o estado atual do editor na pilha
salvar(editor: Editor): void {
this.pilha.push(editor.salvarEstado());
}
// Restaura o último estado salvo
desfazer(editor: Editor): boolean {
const snapshot = this.pilha.pop();
if (!snapshot) return false;
// O Caretaker devolve o Memento ao Originator — nunca o inspeciona.
editor.restaurarEstado(snapshot);
return true;
}
tamanho(): number { return this.pilha.length; }
}
// ── Uso ──────────────────────────────────────────────────────
const editor = new Editor();
const historico = new Historico();
// Estado 1: vazio
historico.salvar(editor);
console.log(`[0] ${editor}`); // "" cursor=0
editor.inserir('Olá');
historico.salvar(editor);
console.log(`[1] ${editor}`); // "Olá" cursor=3
editor.inserir(', mundo');
historico.salvar(editor);
console.log(`[2] ${editor}`); // "Olá, mundo" cursor=10
editor.selecionarIntervalo(4, 9);
historico.salvar(editor);
console.log(`[3] ${editor}`); // "Olá, mundo" cursor=10 [sel 4-9]
// Undo: volta ao estado 3 (antes da seleção)
historico.desfazer(editor);
console.log(`Undo → ${editor}`); // "Olá, mundo" cursor=10
// Undo: volta ao estado 2 (antes de ", mundo")
historico.desfazer(editor);
console.log(`Undo → ${editor}`); // "Olá" cursor=3
// Undo: volta ao estado 1 (vazio)
historico.desfazer(editor);
console.log(`Undo → ${editor}`); // "" cursor=0
console.log(`Snapshots restantes: ${historico.tamanho()}`); // 0
<?php
// ── Memento — snapshot opaco do Editor ────────────────────────
class EditorSnapshot
{
public function __construct(
private readonly string $conteudo,
private readonly int $cursorPos,
private readonly ?array $selecao // [int, int] | null
) {}
/**
* Recupera o estado — apenas o Editor deve chamar este método.
* @return array{conteudo: string, cursorPos: int, selecao: ?array}
*/
public function _recuperar(): array
{
return [
'conteudo' => $this->conteudo,
'cursorPos' => $this->cursorPos,
'selecao' => $this->selecao,
];
}
}
// ── Originator — cria e restaura Mementos ────────────────────
class Editor
{
private string $conteudo = '';
private int $cursorPos = 0;
private ?array $selecao = null; // [int, int] | null
public function inserir(string $texto): void
{
$this->conteudo =
substr($this->conteudo, 0, $this->cursorPos)
. $texto
. substr($this->conteudo, $this->cursorPos);
$this->cursorPos += strlen($texto);
}
public function deletarAnterior(): void
{
if ($this->cursorPos === 0) return;
$this->conteudo =
substr($this->conteudo, 0, $this->cursorPos - 1)
. substr($this->conteudo, $this->cursorPos);
$this->cursorPos--;
}
public function moverCursor(int $pos): void
{
$this->cursorPos = max(0, min($pos, strlen($this->conteudo)));
}
public function selecionarIntervalo(int $inicio, int $fim): void
{
$this->selecao = [$inicio, $fim];
}
// Cria snapshot do estado COMPLETO
public function salvarEstado(): EditorSnapshot
{
return new EditorSnapshot(
$this->conteudo,
$this->cursorPos,
$this->selecao
);
}
// Restaura a partir de um snapshot
public function restaurarEstado(EditorSnapshot $snapshot): void
{
$estado = $snapshot->_recuperar();
$this->conteudo = $estado['conteudo'];
$this->cursorPos = $estado['cursorPos'];
$this->selecao = $estado['selecao'];
}
public function __toString(): string
{
$sel = $this->selecao
? " [sel {$this->selecao[0]}-{$this->selecao[1]}]"
: '';
return "\"{$this->conteudo}\" cursor={$this->cursorPos}{$sel}";
}
}
// ── Caretaker — armazena histórico de snapshots ───────────────
class Historico
{
/** @var EditorSnapshot[] */
private array $pilha = [];
public function salvar(Editor $editor): void
{
$this->pilha[] = $editor->salvarEstado();
}
public function desfazer(Editor $editor): bool
{
$snapshot = array_pop($this->pilha);
if ($snapshot === null) return false;
// O Caretaker devolve o Memento ao Originator — nunca o inspeciona.
$editor->restaurarEstado($snapshot);
return true;
}
public function tamanho(): int
{
return count($this->pilha);
}
}
// ── Uso ──────────────────────────────────────────────────────
$editor = new Editor();
$historico = new Historico();
$historico->salvar($editor);
echo "[0] {$editor}\n"; // "" cursor=0
$editor->inserir('Olá');
$historico->salvar($editor);
echo "[1] {$editor}\n"; // "Olá" cursor=3
$editor->inserir(', mundo');
$historico->salvar($editor);
echo "[2] {$editor}\n"; // "Olá, mundo" cursor=10
$editor->selecionarIntervalo(4, 9);
$historico->salvar($editor);
echo "[3] {$editor}\n"; // "Olá, mundo" cursor=10 [sel 4-9]
$historico->desfazer($editor);
echo "Undo → {$editor}\n"; // "Olá, mundo" cursor=10
$historico->desfazer($editor);
echo "Undo → {$editor}\n"; // "Olá" cursor=3
$historico->desfazer($editor);
echo "Undo → {$editor}\n"; // "" cursor=0
echo "Snapshots restantes: " . $historico->tamanho() . "\n"; // 0
Exemplo 2 — Configuração de aplicação com checkpoints nomeados
O Memento não se limita a editores. Um Caretaker com suporte a checkpoints nomeados (em vez de uma pilha anônima) permite salvar e restaurar estados com semântica clara — como "configuração de fábrica" vs "configuração do usuário" — sem expor os campos internos do objeto de configuração.
// ── Memento de configuração ───────────────────────────────────
class ConfigSnapshot {
constructor(
private readonly dados: Readonly<Record<string, unknown>>
) {}
_recuperar(): Record<string, unknown> {
return { ...this.dados }; // cópia defensiva
}
}
// ── Originator ────────────────────────────────────────────────
class ConfiguracaoApp {
private dados: Record<string, unknown> = {
tema: 'claro',
idioma: 'pt-BR',
fontSize: 14,
notificacoes: true,
};
set(chave: string, valor: unknown): void {
this.dados[chave] = valor;
}
get(chave: string): unknown {
return this.dados[chave];
}
salvarEstado(): ConfigSnapshot {
return new ConfigSnapshot({ ...this.dados });
}
restaurarEstado(snapshot: ConfigSnapshot): void {
this.dados = snapshot._recuperar();
}
exibir(): void {
console.log(JSON.stringify(this.dados, null, 2));
}
}
// ── Caretaker com checkpoints nomeados ────────────────────────
class GerenciadorCheckpoints {
private readonly checkpoints = new Map<string, ConfigSnapshot>();
salvar(nome: string, config: ConfiguracaoApp): void {
this.checkpoints.set(nome, config.salvarEstado());
console.log(`Checkpoint "${nome}" salvo.`);
}
restaurar(nome: string, config: ConfiguracaoApp): boolean {
const snapshot = this.checkpoints.get(nome);
if (!snapshot) {
console.warn(`Checkpoint "${nome}" não encontrado.`);
return false;
}
config.restaurarEstado(snapshot);
console.log(`Checkpoint "${nome}" restaurado.`);
return true;
}
listar(): string[] {
return [...this.checkpoints.keys()];
}
}
// ── Uso ──────────────────────────────────────────────────────
const config = new ConfiguracaoApp();
const gerenciador = new GerenciadorCheckpoints();
// Salva o estado de fábrica
gerenciador.salvar('fabrica', config);
// Usuário personaliza
config.set('tema', 'escuro');
config.set('fontSize', 18);
config.set('idioma', 'en-US');
gerenciador.salvar('usuario', config);
// Mais ajustes — sem salvar
config.set('notificacoes', false);
config.set('fontSize', 22);
console.log('\nEstado atual:');
config.exibir();
// { tema: 'escuro', idioma: 'en-US', fontSize: 22, notificacoes: false }
// Restaura configuração do usuário
gerenciador.restaurar('usuario', config);
config.exibir();
// { tema: 'escuro', idioma: 'en-US', fontSize: 18, notificacoes: true }
// Restaura fábrica
gerenciador.restaurar('fabrica', config);
config.exibir();
// { tema: 'claro', idioma: 'pt-BR', fontSize: 14, notificacoes: true }
console.log('Checkpoints disponíveis:', gerenciador.listar());
// ['fabrica', 'usuario']
<?php
// ── Memento de configuração ───────────────────────────────────
class ConfigSnapshot
{
public function __construct(
private readonly array $dados
) {}
/** @return array<string, mixed> */
public function _recuperar(): array
{
return $this->dados; // arrays PHP são copiados por valor
}
}
// ── Originator ────────────────────────────────────────────────
class ConfiguracaoApp
{
/** @var array<string, mixed> */
private array $dados = [
'tema' => 'claro',
'idioma' => 'pt-BR',
'fontSize' => 14,
'notificacoes' => true,
];
public function set(string $chave, mixed $valor): void
{
$this->dados[$chave] = $valor;
}
public function get(string $chave): mixed
{
return $this->dados[$chave] ?? null;
}
public function salvarEstado(): ConfigSnapshot
{
return new ConfigSnapshot($this->dados);
}
public function restaurarEstado(ConfigSnapshot $snapshot): void
{
$this->dados = $snapshot->_recuperar();
}
public function exibir(): void
{
echo json_encode($this->dados, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
}
}
// ── Caretaker com checkpoints nomeados ────────────────────────
class GerenciadorCheckpoints
{
/** @var array<string, ConfigSnapshot> */
private array $checkpoints = [];
public function salvar(string $nome, ConfiguracaoApp $config): void
{
$this->checkpoints[$nome] = $config->salvarEstado();
echo "Checkpoint \"{$nome}\" salvo.\n";
}
public function restaurar(string $nome, ConfiguracaoApp $config): bool
{
if (!isset($this->checkpoints[$nome])) {
echo "Checkpoint \"{$nome}\" não encontrado.\n";
return false;
}
$config->restaurarEstado($this->checkpoints[$nome]);
echo "Checkpoint \"{$nome}\" restaurado.\n";
return true;
}
/** @return string[] */
public function listar(): array
{
return array_keys($this->checkpoints);
}
}
// ── Uso ──────────────────────────────────────────────────────
$config = new ConfiguracaoApp();
$gerenciador = new GerenciadorCheckpoints();
$gerenciador->salvar('fabrica', $config);
$config->set('tema', 'escuro');
$config->set('fontSize', 18);
$config->set('idioma', 'en-US');
$gerenciador->salvar('usuario', $config);
$config->set('notificacoes', false);
$config->set('fontSize', 22);
echo "\nEstado atual:\n";
$config->exibir();
$gerenciador->restaurar('usuario', $config);
$config->exibir();
$gerenciador->restaurar('fabrica', $config);
$config->exibir();
echo "Checkpoints: " . implode(', ', $gerenciador->listar()) . "\n";
// fabrica, usuario
Quando usar
- Quando você precisa de undo/redo por snapshot: o caso central do padrão. Se o estado do Originator é suficientemente compacto ou a profundidade de undo é limitada, Memento é mais simples de implementar do que Command com undo incremental para cada tipo de operação.
- Quando o estado do objeto é rico e interdependente: se múltiplos campos precisam ser restaurados em conjunto para garantir consistência (ex.: conteúdo + cursor + seleção no editor), um snapshot completo é mais seguro do que reverter campo por campo.
- Quando você precisa de checkpoints ou transações: salvar o estado antes de uma operação potencialmente destrutiva e restaurar em caso de falha — sem expor os internos do objeto a quem faz o gerenciamento.
- Quando o encapsulamento do Originator deve ser preservado: o Caretaker precisa guardar o estado mas não deve conhecer sua estrutura interna. O Memento cria a barreira de abstração necessária.
Quando evitar
- Quando o estado do Originator é grande: cada snapshot é uma cópia do estado completo. Para objetos com megabytes de dados, manter um histórico de snapshots pode esgotar a memória. Considere snapshots incrementais, compressão ou Command com undo incremental.
- Quando a profundidade de undo é ilimitada e o objeto é grande: a combinação de estado grande + muitos níveis de undo torna o Memento proibitivo em memória. Defina um limite máximo de snapshots no Caretaker.
- Quando linguagens dinâmicas tornam o encapsulamento trivial de violar: em PHP e JavaScript, o encapsulamento do Memento é convencional, não aplicado pelo compilador. Se o Caretaker pode acessar os campos do Memento via reflexão, a barreira conceitual do padrão perde força.
Prós e contras
Prós
- Preserva o encapsulamento do Originator — o Caretaker não conhece a estrutura interna do estado.
- Simplifica o Originator: a lógica de undo não fica espalhada em cada operação — é centralizada em salvarEstado/restaurarEstado.
- Permite undo de múltiplos níveis e checkpoints nomeados com o mesmo mecanismo.
- O Caretaker pode limitar a profundidade do histórico descartando Mementos antigos — sem alterar o Originator.
Contras
- Alto consumo de memória para Originators com estado grande — cada snapshot é uma cópia completa.
- O encapsulamento do Memento é difícil de aplicar em linguagens sem suporte a classes internas ou pacotes (como TypeScript e PHP) — é convencional, não forçado pelo compilador.
- Se o Originator muda sua estrutura interna, o formato dos Mementos existentes pode se tornar incompatível (problema de versionamento).
- Requer cópia defensiva do estado para evitar que o Memento compartilhe referências mutáveis com o Originator.
Armadilhas comuns
1. Caretaker que inspeciona o Memento (viola encapsulamento)
O erro fundamental do padrão: o Caretaker chama getters do Memento para ler campos, exibir informações ou tomar decisões. Isso cria acoplamento entre o Caretaker e a estrutura interna do Originator — exatamente o que o padrão visa evitar. O Caretaker deve tratar o Memento como uma caixa preta: recebe, armazena, devolve. Nada mais.
Sinal de alerta: se o Caretaker tem um
if (snapshot.getConteudo().length > 0) em algum lugar,
o encapsulamento foi violado.
2. Referências mutáveis compartilhadas (snapshot raso)
A armadilha técnica mais crítica: o salvarEstado() copia
referências em vez de valores. Se o estado do Originator contém objetos
ou arrays mutáveis e o Memento guarda a referência (não uma cópia
profunda), o snapshot refletirá mudanças posteriores — tornando-o inútil
para restauração. A regra de ouro: o Memento deve ser imutável e conter
apenas cópias dos dados, nunca as referências originais.
// ERRADO: referência compartilhada — o snapshot muda com o Originator
class EditorSnapshot {
constructor(readonly dados: string[]) {} // referência!
}
const snap = new EditorSnapshot(editor.linhas); // editor.linhas e snap.dados apontam para o mesmo array
editor.linhas.push('nova linha'); // corrompeu o snapshot!
// CORRETO: cópia defensiva
class EditorSnapshot {
constructor(readonly dados: readonly string[]) {}
}
const snap = new EditorSnapshot([...editor.linhas]); // cópia — independente
3. Mementos incompatíveis após refatoração (versionamento)
Se o Originator é serializado para persistência (arquivos de save de jogo, sessões de usuário) e o formato do Memento muda com uma refatoração, snapshots antigos podem se tornar irrestauráveis. Considere incluir um número de versão no Memento e implementar migração quando o formato muda — especialmente se Mementos são persistidos além da sessão atual.
4. Histórico sem limite de tamanho
Um Caretaker que aceita Mementos ilimitadamente pode esgotar memória gradualmente. Defina um limite máximo (ex.: 50 snapshots) no Caretaker e descarte os mais antigos quando o limite for atingido. Isso é uma decisão do Caretaker — o Originator e o Memento não precisam saber.
Padrões relacionados
O Memento interage com padrões de comportamento que também lidam com histórico de ações e gerenciamento de estado:
O Command é o complemento natural e mais frequente do
Memento. A distinção crítica: Command desfaz uma ação específica de forma
incremental — cada Command sabe exatamente como reverter o que fez.
Memento restaura um snapshot completo do estado — não desfaz uma ação,
retorna o objeto a um ponto anterior no tempo. Os dois são complementares:
um Command pode usar originator.salvarEstado() em seu
execute() e originator.restaurarEstado(snapshot)
em seu undo(), delegando ao próprio Originator a
responsabilidade de se serializar — sem que o Command precise conhecer
os campos internos do Receiver. O State também gerencia
o estado de um objeto, mas seu foco é representar estados discretos com
transições explícitas, não capturar e restaurar estados arbitrários.
Memento pode complementar o State quando é necessário retornar a um estado
anterior após uma transição — o Memento guarda o snapshot do StateContext
antes da transição.