Menu
Coddy logo textTech

Padrão Command

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 94 de 110.

O padrão Command encapsula uma solicitação como um objeto, permitindo que você parametrize clientes com diferentes solicitações, enfileire operações ou suporte a funcionalidade de desfazer. Em vez de chamar um método diretamente, você envolve a ação em um objeto de comando que pode ser armazenado, passado adiante ou executado posteriormente.

O padrão envolve três componentes principais: uma interface Command com um método execute, Concrete Commands que implementam ações específicas e um Receiver que realiza o trabalho real. Um Invoker opcional dispara comandos sem saber o que eles fazem:

abstract class Command {
  void execute();
}

class Light {
  void turnOn() => print('Light is ON');
  void turnOff() => print('Light is OFF');
}

class TurnOnCommand implements Command {
  final Light light;
  TurnOnCommand(this.light);

  @override
  void execute() => light.turnOn();
}

class TurnOffCommand implements Command {
  final Light light;
  TurnOffCommand(this.light);

  @override
  void execute() => light.turnOff();
}

class RemoteControl {
  Command? _command;

  void setCommand(Command command) {
    _command = command;
  }

  void pressButton() {
    _command?.execute();
  }
}

void main() {
  var light = Light();
  var remote = RemoteControl();

  remote.setCommand(TurnOnCommand(light));
  remote.pressButton();  // Light is ON

  remote.setCommand(TurnOffCommand(light));
  remote.pressButton();  // Light is OFF
}

O RemoteControl não sabe nada sobre luzes - ele apenas executa qualquer comando que lhe seja dado. Este desacoplamento torna fácil adicionar novos comandos sem modificar o invocador. O padrão Command é ideal para implementar sistemas de desfazer/refazer, filas de tarefas ou gravação de macros onde você precisa tratar ações como objetos de primeira classe.

challenge icon

Desafio

Fácil

Vamos construir um sistema de comandos para um editor de texto usando o padrão Command! Você criará um sistema que encapsula operações de texto como objetos de comando, permitindo que um editor execute diferentes ações sem conhecer os detalhes de cada operação. Este é um caso de uso clássico para o padrão Command - pense nele como um sistema de desfazer/refazer onde cada ação é armazenada como um objeto.

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

  • commands.dart: Crie seu sistema de comandos aqui. Comece com uma classe abstrata Command que declara um método execute(). Em seguida, crie uma classe TextDocument (o receptor) que contém um campo String content (inicialmente vazio) e possui três métodos:
    • append(String text) - adiciona texto ao final do conteúdo
    • clear() - define o conteúdo como uma string vazia
    • show() - imprime Document: [content]
    Agora implemente três comandos concretos que funcionam com um TextDocument:
    • AppendCommand - recebe um documento e um texto em seu construtor, e anexa o texto quando executado
    • ClearCommand - recebe um documento em seu construtor, e o limpa quando executado
    • ShowCommand - recebe um documento em seu construtor, e mostra seu conteúdo quando executado
    Finalmente, crie uma classe TextEditor (o invocador) que armazena uma lista de comandos. Ela deve ter um método addCommand(Command command) para enfileirar comandos, e um método runAll() que executa todos os comandos enfileirados em ordem.
  • main.dart: Importe seu arquivo de comandos e demonstre o padrão Command. Crie um TextDocument e um TextEditor. Enfileire os seguintes comandos em ordem: append Hello, append World, mostre o documento, append !, mostre novamente, limpe o documento e mostre uma última vez. Em seguida, execute todos os comandos enfileirados.

Observe como o TextEditor não sabe nada sobre anexar, limpar ou mostrar - ele apenas executa quaisquer comandos que lhe sejam dados. Este desacoplamento é a essência do padrão Command!

Saída esperada:

Document: Hello World
Document: Hello World!
Document: 

Folha de consulta

O padrão Command encapsula uma solicitação como um objeto, permitindo que você parametrize clientes com diferentes solicitações, enfileire operações ou suporte funcionalidades de desfazer.

O padrão envolve três componentes principais:

  • Interface Command com um método execute
  • Concrete Commands que implementam ações específicas
  • Receiver que realiza o trabalho real
  • Invoker (opcional) que dispara comandos sem saber o que eles fazem

Estrutura básica:

abstract class Command {
  void execute();
}

class Receiver {
  void action() => print('Action performed');
}

class ConcreteCommand implements Command {
  final Receiver receiver;
  ConcreteCommand(this.receiver);

  @override
  void execute() => receiver.action();
}

class Invoker {
  Command? _command;

  void setCommand(Command command) {
    _command = command;
  }

  void trigger() {
    _command?.execute();
  }
}

Exemplo com um sistema de controle de luz:

class Light {
  void turnOn() => print('Light is ON');
  void turnOff() => print('Light is OFF');
}

class TurnOnCommand implements Command {
  final Light light;
  TurnOnCommand(this.light);

  @override
  void execute() => light.turnOn();
}

class RemoteControl {
  Command? _command;

  void setCommand(Command command) {
    _command = command;
  }

  void pressButton() {
    _command?.execute();
  }
}

void main() {
  var light = Light();
  var remote = RemoteControl();

  remote.setCommand(TurnOnCommand(light));
  remote.pressButton();  // Light is ON
}

O invoker não sabe nada sobre o receiver - ele apenas executa qualquer comando que lhe seja dado. Este desacoplamento facilita a adição de novos comandos sem modificar o invoker.

Experimente você mesmo

import 'commands.dart';

void main() {
  // TODO: Criar uma instância de TextDocument

  // TODO: Criar uma instância de TextEditor

  // TODO: Enfileirar os seguintes comandos em ordem:
  // 1. Adicionar "Hello"
  // 2. Adicionar " World"
  // 3. Mostrar o documento
  // 4. Adicionar "!"
  // 5. Mostrar o documento novamente
  // 6. Limpar o documento
  // 7. Mostrar o documento uma última vez

  // TODO: Executar todos os comandos enfileirados
}
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