Entendendo o padrão de projeto Command em Java

Por Gaspar Barancelli Junior em 07 de março de 2023

Com o padrão de projeto Command, você pode encapsular uma solicitação como um objeto, permitindo que você parametrize clientes com diferentes solicitações, enfileire ou registre solicitações e suporte operações que podem ser desfeitas.

Imagine que você esteja desenvolvendo uma aplicação de edição de texto e queira permitir que o usuário desfaça e refaça suas ações. O padrão de projeto Command pode ser usado para implementar essa funcionalidade.

A ideia básica é encapsular cada ação do usuário como um objeto Command, que armazena informações sobre a ação e fornece um método para executá-la e outro para desfazê-la. Esses objetos Command são então enfileirados ou registrados em um objeto invocador, que é responsável por executá-los e desfazê-los.

Exemplo de implementação em Java

Veja um exemplo de código em Java para entender melhor como funciona o padrão de projeto Command:

// Interface que define o método execute() que deve ser implementado por todos os comandos
public interface Command {
    void execute();
    void undo();
}

// Implementação concreta de um comando que adiciona um caractere ao texto
public class AddCharCommand implements Command {
    private final char character;
    private final TextEditor textEditor;

    public AddCharCommand(char character, TextEditor textEditor) {
        this.character = character;
        this.textEditor = textEditor;
    }

    public void execute() {
        textEditor.addChar(character);
    }

    public void undo() {
        textEditor.deleteChar();
    }
}

// Implementação concreta de um comando que remove um caractere do texto
public class DeleteCharCommand implements Command {
    private final TextEditor textEditor;
    private char lastDeletedChar;

    public DeleteCharCommand(TextEditor textEditor) {
        this.textEditor = textEditor;
    }

    public void execute() {
        lastDeletedChar = textEditor.deleteChar();
    }

    public void undo() {
        textEditor.addChar(lastDeletedChar);
    }
}

// Classe invocadora que mantém uma lista de comandos executados e permite desfazê-los
public class CommandInvoker {
    private final List<Command> executedCommands = new ArrayList<>();

    public void executeCommand(Command command) {
        command.execute();
        executedCommands.add(command);
    }

    public void undoLastCommand() {
        if (!executedCommands.isEmpty()) {
            Command lastCommand = executedCommands.remove(executedCommands.size() - 1);
            lastCommand.undo();
        }
    }
}

// Classe que representa o editor de texto
public class TextEditor {
    private final StringBuilder text = new StringBuilder();

    public void addChar(char c) {
        text.append(c);
    }

    public char deleteChar() {
        int lastIndex = text.length() - 1;
        char lastChar = text.charAt(lastIndex);
        text.deleteCharAt(lastIndex);
        return lastChar;
    }

    public String getText() {
        return text.toString();
    }
}

// Exemplo de uso do padrão de projeto Command
public class Application {
    public static void main(String[] args) {
        TextEditor textEditor = new TextEditor();
        CommandInvoker commandInvoker = new CommandInvoker();

        commandInvoker.executeCommand(new AddCharCommand('b', textEditor));
        commandInvoker.executeCommand(new AddCharCommand('l', textEditor));
        commandInvoker.executeCommand(new AddCharCommand('o', textEditor));
        commandInvoker.executeCommand(new AddCharCommand('g', textEditor));
        // exibe blog
        System.out.println(textEditor.getText());

        commandInvoker.executeCommand(new DeleteCharCommand(textEditor));
        commandInvoker.executeCommand(new DeleteCharCommand(textEditor));
        // exibe bl
        System.out.println(textEditor.getText());

        commandInvoker.undoLastCommand();
        commandInvoker.undoLastCommand();
        // exibe blog
        System.out.println(textEditor.getText());
    }
}

Agora que já vimos o exemplo de implementação do padrão de projeto Command em Java, vamos entender um pouco mais sobre suas vantagens e desvantagens.

Vantagens

  • Separação de responsabilidades: O padrão Command ajuda a separar a lógica do comando da classe que o invoca, reduzindo o acoplamento entre as classes e melhorando a coesão do código.

  • Suporte a desfazer e refazer ações: O padrão Command permite que as ações executadas sejam desfeitas ou refeitas facilmente, já que as informações necessárias para isso são armazenadas nos objetos de comando.

  • Flexibilidade: Como os comandos são objetos, é possível criar novos comandos e adicioná-los ao sistema sem afetar as classes que já existem.

Desvantagens

  • Aumento da complexidade: O padrão Command pode aumentar a complexidade do código, pois exige a criação de muitas classes adicionais para cada comando.

  • Custo em termos de memória: Como os objetos de comando mantêm informações sobre as ações que executam, eles podem consumir mais memória do que uma simples chamada de método.

  • Dificuldade em lidar com comandos assíncronos: O padrão Command é mais adequado para lidar com comandos síncronos, pois não fornece um mecanismo padrão para lidar com comandos assíncronos.

Conclusão

O padrão de projeto Command é uma ótima opção quando se trata de lidar com ações complexas em um sistema. Ele ajuda a separar a lógica do comando da classe que o invoca, permitindo maior flexibilidade e suporte a desfazer e refazer ações. Por outro lado, pode aumentar a complexidade do código e consumir mais memória. No geral, é uma escolha sólida para aplicações que exigem manipulação de comandos complexos.

// Livros recomendados relacionados ao assunto do post

// Compartilhe esse Post