Menu
Coddy logo textTech

Padrão Command

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 96 de 104.

O padrão Command encapsula uma solicitação como um objeto, permitindo parametrizar clientes com diferentes solicitações, enfileirar operações ou oferecer suporte à funcionalidade de desfazer. Ele desacopla o objeto que invoca uma operação daquele que sabe como executá-la.

O padrão tem quatro componentes principais: uma interface Command com um método execute(), Comandos Concretos que implementam ações específicas, um Receptor que realiza o trabalho efetivo e um Invocador que aciona os comandos:

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

// Receiver - sabe como realizar operações
class Light {
public:
    void turnOn() { std::cout << "Light is ON\n"; }
    void turnOff() { std::cout << "Light is OFF\n"; }
};

// Interface 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(); }
};

// Invoker
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 armazena uma referência ao seu receptor e chama o método apropriado quando executado. O invocador não sabe qual ação acontecerá — ele apenas chama execute(). Ao armazenar os comandos em um histórico, você pode implementar facilmente o desfazer chamando o método undo() de cada comando.

Use Command quando precisar enfileirar operações, implementar desfazer/refazer ou desacoplar o remetente de uma solicitação de seu manipulador.

challenge icon

Desafio

Fácil

Vamos criar um Editor de Texto com funcionalidade de desfazer usando o padrão Command. Você criará um sistema no qual as operações de edição (como inserir e excluir texto) são encapsuladas como objetos de comando, permitindo que os usuários executem ações e depois as desfaçam na ordem inversa, assim como em um editor de texto real.

Você organizará seu código em quatro arquivos:

  • TextDocument.h: crie a classe receptora que armazena e manipula o conteúdo real do texto.

    Sua classe TextDocument deve armazenar o conteúdo do documento como uma string. Implemente estes métodos:

    • insertText(const std::string& text): adiciona o texto ao final do conteúdo
    • deleteText(int count): remove os últimos count caracteres do conteúdo (não faz nada se count exceder o tamanho do conteúdo)
    • getContent(): retorna o conteúdo atual como uma referência const
  • Command.h: defina a interface de comando e as classes de comando concretas.

    Crie uma classe abstrata Command com os métodos virtuais puros execute() e undo(), além de um destrutor virtual.

    Em seguida, implemente dois comandos concretos:

    • InsertCommand: recebe uma referência a um TextDocument e uma string para inserir. Quando executado, insere o texto. Quando desfeito, exclui a mesma quantidade de caracteres que foi inserida.
    • DeleteCommand: recebe uma referência a um TextDocument e uma quantidade de caracteres a excluir. Ele precisa armazenar o texto excluído para poder restaurá-lo ao desfazer. Quando executado, remove os caracteres do final. Quando desfeito, insere novamente o texto armazenado.
  • TextEditor.h: crie o invocador que gerencia a execução e o histórico dos comandos.

    Sua classe TextEditor deve manter uma referência a um TextDocument e manter um histórico dos comandos executados usando um vetor de unique_ptr<Command>. Implemente:

    • executeCommand(std::unique_ptr<Command> cmd): executa o comando e o adiciona ao histórico
    • undo(): desfaz o comando mais recente e o remove do histórico (não faz nada se o histórico estiver vazio)
    • showContent(): exibe o conteúdo atual do documento ou [empty] se o conteúdo estiver vazio
  • main.cpp: demonstre o padrão Command com funcionalidade de desfazer.

    Leia três entradas:

    1. Primeiro texto a inserir (string)
    2. Segundo texto a inserir (string)
    3. Número de caracteres a excluir (inteiro)

    Crie um TextDocument e um TextEditor. Em seguida, realize estas operações na ordem indicada, exibindo o conteúdo após cada etapa:

    1. Insira o primeiro texto e exiba o conteúdo
    2. Insira o segundo texto e exiba o conteúdo
    3. Exclua a quantidade especificada de caracteres e exiba o conteúdo
    4. Desfaça uma vez e exiba o conteúdo
    5. Desfaça mais uma vez e exiba o conteúdo

Por exemplo, com as entradas Hello, World e 3:

Hello
Hello World
Hello Wo
Hello World
Hello

Com as entradas Code, Editor e 6:

Code
CodeEditor
Code
CodeEditor
Code

Observe como cada comando sabe como se reverter. O DeleteCommand lembra o que excluiu para poder restaurá-lo, e o InsertCommand sabe exatamente quantos caracteres remover ao desfazer. Esse é o poder do padrão Command: as operações se tornam objetos de primeira classe que podem ser armazenados, executados e revertidos.

Experimente você mesmo

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

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

    // TODO: Crie um TextDocument e um TextEditor
    
    // TODO: Execute as operações em ordem, mostrando o conteúdo após cada uma:
    // 1. Insira o primeiro texto, depois mostre o conteúdo
    // 2. Insira o segundo texto, depois mostre o conteúdo
    // 3. Delete o número especificado de caracteres, depois mostre o conteúdo
    // 4. Desfaça uma vez, depois mostre o conteúdo
    // 5. Desfaça mais uma vez, depois mostre o conteúdo
    
    // Dica: Use std::make_unique<InsertCommand>(...) e 
    //       std::make_unique<DeleteCommand>(...) para criar comandos

    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de C++ online