🚨 ¡Nueva review! ✨ Mi ratón favorito para programar: el Logitech MX Master 3S . ¡Échale un ojo! 👀

El patrón de diseño Command

La forma más elegante de encapsular una acción para ejecutarla más tarde

Escrito por domin el 20 de noviembre de 2025

🎮 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.

Diagrama del patrón Command mostrando un invocador, una interfaz de comando, comandos concretos y un receptor.

🤯 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.

  1. 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.
  2. La Interfaz (el contrato de comando): La norma que dice: “Todo lo que se considere una orden debe tener un método ejecutar()”.
  3. 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.
  4. 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

ReceptorEl Televisor/Editor

Contiene la lógica de negocio; sabe cómo hacer las cosas.

ComandoEl Objeto Subir Volumen

Encapsula la orden y una referencia al receptor.

InvocadorEl 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í”)

⚠️ Advertencias (El “No”)

📈 5. Beneficios Tangibles

  1. Desacoplamiento: El Invocador (el botón) y el Receptor (el editor) no se conocen directamente, haciendo el código más flexible.
  2. Extensibilidad: Añadir un nuevo tipo de acción es tan fácil como crear una nueva clase Comando sin tocar el resto del sistema.
  3. 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 🍻