Saltar al contenido

Command

Un patrón de comportamiento que encapsula una solicitud como un objeto, para poder guardarla, encolarla o deshacerla.

6 min. de lectura

Command es un patrón de comportamiento que convierte una solicitud en un objeto independiente, que se puede parametrizar, guardar en una cola, loguear o deshacer.

El problema

El panel de administración de AndesShop necesita poder deshacer ciertas acciones: aplicar un descuento a un pedido, cancelar un pedido, reponer stock manualmente. Si cada botón de la interfaz llama directamente al método correspondiente (order.applyDiscount(10), order.cancel()), no hay ningún lugar donde registrar qué se hizo ni cómo revertirlo — el “deshacer” tendría que reconstruirse a mano para cada acción distinta.

La solución

Command envuelve cada acción en un objeto propio, con un método execute() que la ejecuta y, opcionalmente, un método undo() que la revierte. La interfaz que dispara las acciones (los botones del panel) deja de llamar métodos de negocio directamente y pasa a ejecutar objetos Command — que se pueden guardar en un historial para deshacer, encolar para ejecutar después, o loguear para auditoría.

Estructura de Command: cada comando guarda el receiver y el estado necesario para revertir la acción; el invoker solo conoce la interfaz.

Ejemplo en Java

interface Command {
    void execute();
    void undo();
}

class ApplyDiscountCommand implements Command {
    private final Order order;
    private final double discountPercent;
    private double previousPrice;

    public ApplyDiscountCommand(Order order, double discountPercent) {
        this.order = order;
        this.discountPercent = discountPercent;
    }

    public void execute() {
        previousPrice = order.getPrice();
        order.applyDiscount(discountPercent);
    }

    public void undo() {
        order.setPrice(previousPrice);
    }
}

// El invocador: no sabe qué hace cada Command, solo sabe ejecutarlo y guardarlo
class CommandHistory {
    private final Deque<Command> history = new ArrayDeque<>();

    public void run(Command command) {
        command.execute();
        history.push(command);
    }

    public void undoLast() {
        if (!history.isEmpty()) {
            history.pop().undo();
        }
    }
}
// Client code
CommandHistory history = new CommandHistory();
history.run(new ApplyDiscountCommand(order, 10));
// ...
history.undoLast(); // revierte el descuento, sin que CommandHistory sepa qué era

Cuándo usarlo

  • Cuando necesitás poder deshacer o rehacer acciones.
  • Cuando querés encolar, programar o loguear solicitudes para ejecutarlas más tarde o en otro proceso.
  • Cuando querés parametrizar un objeto con una acción a ejecutar, en vez de con los datos crudos de esa acción.

Cuándo evitarlo

Si las acciones son simples y no necesitás deshacerlas, encolarlas ni loguearlas, envolverlas en objetos Command agrega una capa que no se aprovecha.

Ventajas y desventajas

VentajasDesventajas
Desacopla quién dispara una acción de quién sabe cómo ejecutarlaAgrega una clase nueva por cada tipo de acción distinta
Permite implementar deshacer/rehacer de forma naturalPuede resultar excesivo para acciones simples que nunca necesitan revertirse
Las acciones se pueden serializar, encolar o loguear como cualquier otro objeto

Relación con otros patrones

  • Memento se usa a menudo junto con Command para poder implementar el undo() guardando una foto del estado anterior, en vez de revertir campo por campo.
  • Chain of Responsibility y Command se combinan cuando una solicitud viaja por una cadena de manejadores, encapsulada como Command.

Te sirvió, compartilo

// ¿te sirvió?
// compartilo