Menu
Coddy logo textTech

Patrón Command

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 96 de 104.

El patrón Command encapsula una solicitud como un objeto, lo que permite parametrizar los clientes con distintas solicitudes, poner operaciones en cola o admitir la funcionalidad de deshacer. Desacopla el objeto que invoca una operación del que sabe cómo realizarla.

El patrón tiene cuatro componentes clave: una interfaz Command con un método execute(), comandos concretos que implementan acciones específicas, un receptor que realiza el trabajo real y un invocador que activa los comandos:

#include <iostream>
#include <memory>
#include <vector>

// Receptor - sabe cómo realizar operaciones
class Light {
public:
    void turnOn() { std::cout << "Light is ON\n"; }
    void turnOff() { std::cout << "Light is OFF\n"; }
};

// Interfaz Command
class Command {
public:
    virtual void execute() = 0;
    virtual void undo() = 0;
    virtual ~Command() = default;
};

// Comandos concretos
class LightOnCommand : public Command {
    Light& light;
public:
    LightOnCommand(Light& l) : light(l) {}
    void execute() override { light.turnOn(); }
    void undo() override { light.turnOff(); }
};

class LightOffCommand : public Command {
    Light& light;
public:
    LightOffCommand(Light& l) : light(l) {}
    void execute() override { light.turnOff(); }
    void undo() override { light.turnOn(); }
};

// Invocador
class RemoteControl {
    std::vector<std::unique_ptr<Command>> history;
public:
    void pressButton(std::unique_ptr<Command> cmd) {
        cmd->execute();
        history.push_back(std::move(cmd));
    }
    
    void pressUndo() {
        if (!history.empty()) {
            history.back()->undo();
            history.pop_back();
        }
    }
};

Cada comando almacena una referencia a su receptor y llama al método apropiado cuando se ejecuta. El invocador no sabe qué acción ocurrirá; simplemente llama a execute(). Al almacenar los comandos en un historial, puedes implementar fácilmente el deshacer llamando al método undo() de cada comando.

Usa Command cuando necesites poner operaciones en cola, implementar deshacer/rehacer o desacoplar el emisor de una solicitud de su controlador.

challenge icon

Desafío

Fácil

Construyamos un Editor de texto con funcionalidad de deshacer mediante el patrón Command. Crearás un sistema en el que las operaciones de edición (como insertar y eliminar texto) se encapsulan como objetos de comando, lo que permite a los usuarios ejecutar acciones y después deshacerlas en orden inverso, igual que en un editor de texto real.

Organizarás tu código en cuatro archivos:

  • TextDocument.h: crea la clase receptora que contiene y manipula el contenido de texto real.

    Tu clase TextDocument debe almacenar el contenido del documento como una cadena. Implementa estos métodos:

    • insertText(const std::string& text): añade el texto al final del contenido
    • deleteText(int count): elimina los últimos count caracteres del contenido (no hace nada si count supera la longitud del contenido)
    • getContent(): devuelve el contenido actual como una referencia const
  • Command.h: define la interfaz de comando y las clases de comando concretas.

    Crea una clase abstracta Command con los métodos virtuales puros execute() y undo(), además de un destructor virtual.

    Después, implementa dos comandos concretos:

    • InsertCommand: recibe una referencia a un TextDocument y una cadena que insertar. Al ejecutarse, inserta el texto. Al deshacerse, elimina el mismo número de caracteres que se insertaron.
    • DeleteCommand: recibe una referencia a un TextDocument y un número de caracteres que eliminar. Debe almacenar el texto eliminado para poder restaurarlo al deshacer. Al ejecutarse, elimina caracteres del final. Al deshacerse, vuelve a insertar el texto almacenado.
  • TextEditor.h: crea el invocador que gestiona la ejecución de comandos y el historial.

    Tu clase TextEditor debe contener una referencia a un TextDocument y mantener un historial de los comandos ejecutados mediante un vector de unique_ptr<Command>. Implementa:

    • executeCommand(std::unique_ptr<Command> cmd): ejecuta el comando y lo añade al historial
    • undo(): deshace el comando más reciente y lo elimina del historial (no hace nada si el historial está vacío)
    • showContent(): muestra el contenido actual del documento, o [empty] si el contenido está vacío
  • main.cpp: demuestra el patrón Command con funcionalidad de deshacer.

    Lee tres entradas:

    1. Primer texto que insertar (cadena)
    2. Segundo texto que insertar (cadena)
    3. Número de caracteres que eliminar (entero)

    Crea un TextDocument y un TextEditor. Después, realiza estas operaciones en orden, mostrando el contenido después de cada paso:

    1. Inserta el primer texto y después muestra el contenido
    2. Inserta el segundo texto y después muestra el contenido
    3. Elimina el número especificado de caracteres y después muestra el contenido
    4. Deshaz una vez y después muestra el contenido
    5. Deshaz una vez más y después muestra el contenido

Por ejemplo, con las entradas Hello, World y 3:

Hello
Hello World
Hello Wo
Hello World
Hello

Con las entradas Code, Editor y 6:

Code
CodeEditor
Code
CodeEditor
Code

Observa cómo cada comando sabe cómo revertirse. DeleteCommand recuerda lo que eliminó para poder restaurarlo, y InsertCommand sabe exactamente cuántos caracteres debe eliminar al deshacer. Este es el poder del patrón Command: las operaciones se convierten en objetos de primera clase que se pueden almacenar, ejecutar y revertir.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include <memory>
#include "TextDocument.h"
#include "Command.h"
#include "TextEditor.h"

int main() {
    // Leer entradas
    std::string firstText;
    std::string secondText;
    int deleteCount;
    
    std::getline(std::cin, firstText);
    std::getline(std::cin, secondText);
    std::cin >> deleteCount;

    // TODO: Crea un TextDocument y un TextEditor
    
    // TODO: Realiza las operaciones en orden, mostrando el contenido después de cada una:
    // 1. Inserta el primer texto, luego muestra el contenido
    // 2. Inserta el segundo texto, luego muestra el contenido
    // 3. Elimina el número especificado de caracteres, luego muestra el contenido
    // 4. Deshaz una vez, luego muestra el contenido
    // 5. Deshaz una vez más, luego muestra el contenido
    
    // Pista: Usa std::make_unique<InsertCommand>(...) y 
    //       std::make_unique<DeleteCommand>(...) para crear comandos

    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C++ online