Saltar al contenido

Memento

Un patrón de comportamiento para guardar el estado de un objeto y restaurarlo después, sin romper su encapsulamiento.

6 min. de lectura

También llamado: Snapshot

Memento es un patrón de comportamiento que permite guardar y restaurar el estado previo de un objeto sin exponer los detalles de su implementación.

El problema

En el panel de administración, un pedido en estado borrador se puede editar varias veces antes de confirmarlo: agregar o quitar productos, cambiar la dirección, ajustar cantidades. El equipo de soporte quiere poder deshacer esas ediciones y volver a una versión anterior del borrador. Guardar el estado accediendo directamente a los campos privados de Order desde afuera rompe su encapsulamiento — y cualquier cambio futuro en la estructura interna de Order obligaría a actualizar también el código que la guarda y restaura.

La solución

Memento delega la captura del estado al propio objeto que lo posee: Order es quien sabe crear una foto de sí mismo (OrderMemento) y quien sabe restaurarse a partir de una foto anterior. El objeto que gestiona el historial de borradores guarda esas fotos, pero nunca mira ni modifica su contenido — solo las pasa de un lado a otro cuando hace falta deshacer.

Estructura de Memento: el originator crea y lee el memento; el caretaker solo lo guarda y lo devuelve, sin poder inspeccionarlo.

Ejemplo en Java

// El Memento: inmutable, solo Order sabe interpretarlo
class OrderMemento {
    private final List<Item> items;
    private final String address;

    OrderMemento(List<Item> items, String address) {
        this.items = List.copyOf(items);
        this.address = address;
    }
}

class Order {
    private List<Item> items = new ArrayList<>();
    private String address;

    public OrderMemento save() {
        return new OrderMemento(items, address);
    }

    public void restore(OrderMemento memento) {
        this.items = new ArrayList<>(memento.items);
        this.address = memento.address;
    }

    public void addItem(Item item) { items.add(item); }
    public void setAddress(String address) { this.address = address; }
}

// El Caretaker: guarda mementos, sin mirar nunca su contenido
class OrderHistory {
    private final Deque<OrderMemento> snapshots = new ArrayDeque<>();

    public void push(OrderMemento memento) {
        snapshots.push(memento);
    }

    public OrderMemento pop() {
        return snapshots.pop();
    }
}
Order draft = new Order();
OrderHistory history = new OrderHistory();

draft.addItem(jacket);
history.push(draft.save()); // guarda el punto de partida

draft.setAddress("wrong address");
// ...si hace falta deshacer:
draft.restore(history.pop());

Cuándo usarlo

  • Cuando necesitás poder deshacer cambios sobre un objeto sin exponer sus detalles internos al código que gestiona el historial.
  • Cuando capturar el estado directamente desde afuera rompería el encapsulamiento del objeto.

Cuándo evitarlo

Guardar mementos de objetos muy grandes o que cambian con mucha frecuencia puede consumir bastante memoria — hay que evaluar el volumen de snapshots que se van a acumular.

Ventajas y desventajas

VentajasDesventajas
Permite deshacer cambios sin romper el encapsulamiento del objeto originalGuardar muchos mementos de objetos grandes puede consumir mucha memoria
El objeto que gestiona el historial no necesita conocer la estructura interna del objeto que respaldaHay que decidir explícitamente cuándo tomar cada snapshot

Relación con otros patrones

  • Se usa a menudo junto con Command para implementar undo(): el Command guarda un Memento antes de ejecutarse, y lo usa para revertir.
  • Se diferencia de Prototype en que Memento captura estado para restaurarlo sobre el mismo objeto más adelante, mientras que Prototype crea un objeto nuevo e independiente a partir de uno existente.

Te sirvió, compartilo

// ¿te sirvió?
// compartilo