🎮 El patrón de diseño Command
El patrón de diseño Command (Comando) es un patrón de comportamiento que toma una acción completa y la encapsula como un objeto. Suena abstracto, pero la idea es simple: queremos separar la orden de quien la da.

🤯 1. El problema que resuelve (El “Dolor”)
Situación inicial: El acoplamiento directo
Imagina que estás construyendo una aplicación de edición de texto. Tienes un botón para “Guardar” y otro para “Imprimir”.
El camino fácil es hacer que el botón llame directamente al método de la clase editor:
// Código acoplado y difícil de reutilizar
// Clase que realmente hace el trabajo (el Receptor)
class EditorTexto {
public function guardar(): void {
echo "Guardando el documento en disco..." . PHP_EOL;
}
public function imprimir(): void {
echo "Imprimiendo el documento..." . PHP_EOL;
}
}
// Clase que da la orden (el Invocador)
class Boton {
private EditorTexto $editor;
public function __construct(EditorTexto $editor) {
$this->editor = $editor;
}
// El Botón sabe EXACTAMENTE qué clase y método llamar
public function clickGuardar(): void {
$this->editor->guardar();
}
}
El problema es que el Boton está pegado a la clase EditorTexto. Si mañana quieres que ese botón ejecute una tarea completamente diferente (como enviar un email), tienes que modificar la clase Boton.
Necesitas un sistema donde quien pulsa el botón no sepa qué se va a ejecutar, solo que se ejecutará algo.
✨ La solución en 60 segundos
El patrón de diseño Command introduce una capa intermedia: el Objeto Comando.
Este patrón consiste en convertir una petición en un objeto independiente. Este objeto lleva consigo toda la información necesaria para ejecutar la acción más tarde, sin que el invocador (el botón) tenga que saber quién o cómo se hace.
🌎 2. Analogía del mundo real: El mando a distancia 📺
Imagina un control remoto de una televisión.
- El Invocador (los diferentes botones del mando): Tú pulsas el botón de “subir volumen”. El botón es el mismo en todos los mandos y solo tiene una tarea: ejecutar. No sabe si el televisor está encendido o qué marca es.
- La Interfaz (el contrato de comando): La norma que dice: “Todo lo que se considere una orden debe tener un método
ejecutar()”. - El Comando Concreto (El objeto subir volumen): Es el chip dentro del mando que encapsula la orden. Contiene dos cosas: la referencia al Televisor (quien recibe la orden) y la acción de subir volumen.
- El Receptor (el televisor): Es la clase que realmente ejecuta la lógica (
$televisor->subirVolumen()).
El botón del mando solo llama a ComandoSubirVolumen->ejecutar(). De esta forma, puedes cambiar el Comando (por ejemplo, a un ComandoCambiarCanal) sin tener que cambiar el botón físico.
Rol en Command | Concepto (analogía) | Tarea principal |
|---|---|---|
| Receptor | El Televisor/Editor | Contiene la lógica de negocio; sabe cómo hacer las cosas. |
| Comando | El Objeto Subir Volumen | Encapsula la orden y una referencia al receptor. |
| Invocador | El Botón del Mando | Solo sabe que debe ejecutar un Comando, no sabe el detalle. |
🧑💻 3. El patrón de diseño Command aplicado al ejemplo.
Vamos a rehacer el ejemplo del editor de texto usando el patrón Command.
<?php
// 🔹 1. Interfaz de Comando (El Contrato)
// Define el método que todos los Comandos deben tener.
interface Comando {
public function ejecutar(): void;
}
// 🔹 2. Receptor (El que sabe hacer las cosas)
// La clase original que contiene la lógica.
class EditorTexto {
public function guardar(): void {
echo " [RECEPTOR] Documento guardado correctamente." . PHP_EOL;
}
public function imprimir(string $tipo): void {
echo " [RECEPTOR] Imprimiendo documento con formato: $tipo." . PHP_EOL;
}
}
// 🔹 3. Comando Concreto: Guardar (Encapsula la petición)
class ComandoGuardar implements Comando {
private EditorTexto $receptor;
// El comando se crea con la referencia al Receptor
public function __construct(EditorTexto $editor) {
$this->receptor = $editor;
}
// Aquí solo llama al método del Receptor.
public function ejecutar(): void {
echo "-> [COMANDO] Orden de Guardar recibida." . PHP_EOL;
$this->receptor->guardar();
}
}
// 🔹 4. Comando Concreto: Imprimir (Puede llevar parámetros)
class ComandoImprimir implements Comando {
private EditorTexto $receptor;
private string $formato;
public function __construct(EditorTexto $editor, string $formato) {
$this->receptor = $editor;
$this->formato = $formato;
}
public function ejecutar(): void {
echo "-> [COMANDO] Orden de Imprimir recibida." . PHP_EOL;
$this->receptor->imprimir($this->formato);
}
}
// 🔹 5. Invocador (El que da la orden)
// El Invocador solo conoce la interfaz genérica "Comando".
class BotonUI {
private Comando $comando;
// Se configura con el Comando que debe ejecutar.
public function setComando(Comando $c): void {
$this->comando = $c;
echo "--- Botón configurado para: " . get_class($c) . " ---" . PHP_EOL;
}
// El botón ejecuta el Comando, sin saber qué hay dentro.
public function click(): void {
echo "[INVOCADOR] Botón pulsado..." . PHP_EOL;
$this->comando->ejecutar();
}
}
// 🔹 6. Uso del patrón (Cliente)
$miEditor = new EditorTexto();
$botonGuardar = new BotonUI();
$botonImprimir = new BotonUI();
// 1. Configurar y usar el comando Guardar
$comandoGuardar = new ComandoGuardar($miEditor);
$botonGuardar->setComando($comandoGuardar);
$botonGuardar->click();
echo PHP_EOL;
// 2. Configurar y usar el comando Imprimir (con un parámetro)
$comandoImprimirPDF = new ComandoImprimir($miEditor, "PDF");
$botonImprimir->setComando($comandoImprimirPDF);
$botonImprimir->click();
// 🖥️ Resultado en consola:
// --- Botón configurado para: ComandoGuardar ---
// [INVOCADOR] Botón pulsado...
// -> [COMANDO] Orden de Guardar recibida.
// [RECEPTOR] Documento guardado correctamente.
//
// --- Botón configurado para: ComandoImprimir ---
// [INVOCADOR] Botón pulsado...
// -> [COMANDO] Orden de Imprimir recibida.
// [RECEPTOR] Imprimiendo documento con formato: PDF.
🚀 El poder de “Deshacer” (Undo)
Dado que una acción es un objeto, podemos añadir un método
deshacer() a la interfaz Comando.
Al ejecutar, guardamos el estado previo. Si el usuario pulsa
“Deshacer”, simplemente llamamos al método deshacer()
en el último objeto Comando ejecutado que guardamos en una lista.
¡Magic! 🧙
🤔 1. ¿Cuál es el principal beneficio de desacoplamiento del patrón Command?
🤔 4. ¿Cuándo Usarlo (y Cuándo No)?
✅ Señales Claras (El “Sí”)
- Cuando necesites implementar operaciones de deshacer/rehacer (Undo/Redo).
- Cuando quieras montar una cola de tareas (o scheduler): guardas los comandos en una lista y los ejecutas uno por uno más tarde.
- Cuando debas crear un registro de actividades (logging) o un historial de las acciones del usuario.
- Cuando el invocador debe cambiar su comportamiento en tiempo de ejecución (como nuestro
BotonUI).
⚠️ Advertencias (El “No”)
- No lo uses si tu aplicación es muy simple y las acciones nunca cambian. Introducir cuatro clases para una sola llamada de método es sobre-ingeniería.
- Si solo tienes una acción simple y no necesitas funcionalidades como deshacer, colas, o registros. La complejidad añadida no se justifica.
📈 5. Beneficios Tangibles
- Desacoplamiento: El Invocador (el botón) y el Receptor (el editor) no se conocen directamente, haciendo el código más flexible.
- Extensibilidad: Añadir un nuevo tipo de acción es tan fácil como crear una nueva clase
Comandosin tocar el resto del sistema. - Abstracción de la Acción: Puedes tratar una orden como una pieza de datos, lo que permite pasarla como argumento, almacenarla o retrasar su ejecución.
🖼️ 6. El Resumen Memorable
Command es el camarero de tu código: toma tu orden (la acción y sus parámetros), la encapsula en un ticket (el objeto Comando), y se asegura de que la cocina (el Receptor) la prepare, sin que tú (el Invocador) tengas que entrar en la cocina.
🤔 2. ¿En qué escenario el patrón Command es fundamental?
¡EA! Saluditos y a seguir programando con una birrita 🍻