Command Palette

Search for a command to run...

Command Pattern: Encapsulando Acciones como Objetos

Aprende a encapsular solicitudes como objetos usando el patrón Command, permitiendo ejecución diferida, colas de comandos, undo/redo y desacoplamiento entre invocadores y receptores.

Lectura: 15 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • El Command Pattern encapsula una solicitud como un objeto
  • Cada comando tiene un método execute para realizar la acción
  • Los comandos pueden ejecutarse inmediatamente o diferirse
  • Permite implementar funcionalidades como undo/redo y colas de comandos
  • Desacopla el invocador de la acción específica que se ejecuta

Introducción al Command Pattern

El Command Pattern es un patrón de diseño comportamental que convierte una solicitud en un objeto independiente que contiene toda la información sobre la solicitud. Esta transformación te permite parametrizar clientes con diferentes solicitudes, poner en cola o registrar solicitudes, y soportar operaciones deshacibles.

En JavaScript, el patrón Command es fundamental para implementar sistemas de undo/redo, colas de tareas, programación de acciones, y cualquier escenario donde necesites encapsular una acción para ejecutarla más tarde o múltiples veces. Este patrón desacopla completamente al objeto que invoca la acción del objeto que la ejecuta.

Command vs Observer Pattern

El Command Pattern encapsula una acción específica que se ejecuta cuando se invoca, mientras que el Observer Pattern establece una relación uno a muchos donde los observadores reaccionan a cambios en el sujeto. El Command es ideal para acciones discretas y reversibles, mientras que el Observer es mejor para eventos y notificaciones continuas.

Concepto del Command Pattern

El patrón Command consta de cuatro componentes principales: el Command (interfaz con el método execute), el ConcreteCommand (implementación específica), el Invoker (que invoca el comando), y el Receiver (que realiza la acción real). El invoker no necesita conocer los detalles de la acción, solo necesita llamar al método execute del comando.

Implementación Básica

Una implementación básica del Command Pattern requiere definir una interfaz de comando con un método execute, crear comandos concretos que encapsulen acciones específicas, y un invoker que ejecute los comandos sin conocer los detalles de implementación.

command-basico.js
Loading code...

Este ejemplo muestra cómo implementar el patrón Command para controlar un dispositivo doméstico. Cada comando encapsula una acción específica (encender, apagar, ajustar volumen), y el control remoto (invoker) puede ejecutar estos comandos sin conocer los detalles del dispositivo.

Encapsulación de Acciones

El Command Pattern encapsula completamente una acción, incluyendo el receptor, los parámetros y la lógica de ejecución. Esto permite que los comandos sean pasados como parámetros, almacenados en estructuras de datos, o ejecutados en momentos diferentes sin perder la información necesaria para realizar la acción.

Undo y Redo

Una de las aplicaciones más poderosas del patrón Command es implementar funcionalidades de undo y redo. Cada comando puede almacenar el estado anterior antes de ejecutarse, permitiendo revertir la acción ejecutando el método undo. Esto es fundamental en aplicaciones de edición, formularios y cualquier sistema donde los usuarios necesiten deshacer acciones.

undo-redo.js
Loading code...

Este ejemplo demuestra cómo implementar un sistema de undo/redo usando el patrón Command. Cada comando almacena el estado anterior antes de ejecutarse, y el invoker mantiene dos pilas: una para comandos ejecutados (undo stack) y otra para comandos deshechos (redo stack).

  • Cada comando encapsula una acción con su método <code>execute()</code>
  • El invoker ejecuta comandos sin conocer los detalles de implementación
  • Los comandos pueden almacenarse para ejecución diferida
  • El método <code>undo()</code> permite revertir acciones ejecutadas
  • Este patrón facilita implementar colas de comandos y programación de tareas

Ventajas del Command Pattern

El Command Pattern ofrece ventajas significativas cuando necesitas encapsular acciones, implementar undo/redo, o gestionar colas de operaciones. Es especialmente valioso en aplicaciones interactivas donde los usuarios necesitan revertir acciones.

  • Desacopla completamente el invocador de la acción que se ejecuta
  • Permite implementar undo/redo almacenando el estado anterior
  • Los comandos pueden ejecutarse inmediatamente o diferirse para más tarde
  • Facilita crear colas de tareas, programación de acciones y procesamiento por lotes
  • Los comandos son objetos de primera clase que pueden pasarse, almacenarse y registrarse
  • Simplifica la implementación de macros combinando múltiples comandos

Poder del Undo/Redo

El Command Pattern es la forma estándar de implementar undo/redo en aplicaciones. Cada comando almacena lo necesario para revertir su acción, y el invoker mantiene pilas de comandos ejecutados y deshechos. Esto es fundamental en editores de texto, herramientas de diseño y aplicaciones empresariales.

El patrón Command se combina perfectamente con otros patrones como Memento (para almacenar estados), Composite (para crear macros de comandos), y Chain of Responsibility (para procesar comandos en cadena). Esta combinación crea sistemas altamente flexibles.

Casos de Uso Reales

El Command Pattern tiene numerosas aplicaciones en desarrollo web moderno. Desde sistemas de undo/redo en editores de texto hasta colas de tareas asíncronas y programación de acciones, este patrón es ideal para cualquier escenario donde necesites encapsular acciones para ejecutarlas más tarde o múltiples veces.

casos-uso-command.js
Loading code...

Este ejemplo muestra cómo usar el patrón Command para implementar una cola de tareas asíncronas. Los comandos se agregan a la cola y se ejecutan secuencialmente, permitiendo gestionar operaciones asíncronas de forma ordenada y controlada.

Errores Comunes

Al implementar el patrón Command, existen varios errores comunes que pueden causar comportamientos inesperados, memory leaks, o código difícil de mantener. Es importante conocer estos patrones de error para evitarlos.

No Implementar Correctamente el Método Undo

El error más común es crear comandos con undo que no funciona correctamente. Si el método undo no revierte completamente la acción del execute, el estado del sistema queda inconsistente y la funcionalidad de deshacer es inútil.

errores-command.js
Loading code...

El primer error en el ejemplo muestra un comando cuyo undo simplemente imprime un mensaje pero no revierte la acción. La solución es almacenar la información necesaria durante execute para poder revertir completamente la acción en undo.

Memory Leaks por Historial Ilimitado

En sistemas con undo/redo, mantener un historial ilimitado de comandos causa memory leaks significativos. Cada comando puede mantener referencias a objetos grandes, y sin un límite, el historial crece indefinidamente consumiendo memoria.

El segundo error muestra cómo un invoker sin límite de historial acumula comandos indefinidamente. La solución es implementar un límite máximo de comandos en el historial y eliminar los más antiguos cuando se alcanza ese límite.

Advertencia: Memory Leaks

En aplicaciones de larga duración, un historial ilimitado puede consumir gigabytes de memoria. Implementa siempre un límite razonable (50-100 comandos) y elimina los comandos más antiguos. También considera implementar comandos que liberen referencias a datos grandes después de ejecutarse.

No Manejar Errores en Comandos Asíncronos

Los comandos asíncronos que no manejan excepciones correctamente causan errores silenciosos difíciles de depurar. Es crucial usar try/catch en comandos async y propagar los errores para que el invoker pueda manejarlos apropiadamente.

El tercer error demuestra cómo un comando asíncrono sin manejo de errores deja al invoker sin información sobre fallos. La solución es capturar excepciones, registrarlas para debugging, y propagarlas para que el código llamador pueda decidir cómo responder.

Resumen: Command Pattern

Conceptos principales:

  • El Command Pattern encapsula una solicitud como un objeto independiente
  • Cada comando tiene métodos execute y opcionalmente undo
  • El invoker ejecuta comandos sin conocer los detalles de implementación
  • Los comandos pueden ejecutarse inmediatamente o diferirse
  • Permite implementar undo/redo, colas y programación de acciones

Mejores prácticas:

  • Usa comandos para acciones que necesitan ejecutarse más tarde
  • Implementa undo solo para comandos que pueden revertirse
  • Limita el tamaño del historial de comandos para evitar memory leaks
  • Documenta claramente qué hace cada comando
  • Considera usar comandos asíncronos para operaciones de larga duración