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.
Desafio
FácilVamos 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 abstrataCommandque declara um métodoexecute(). Em seguida, crie uma classeTextDocument(o receptor) que contém um campoString content(inicialmente vazio) e possui três métodos:append(String text)- adiciona texto ao final do conteúdoclear()- define o conteúdo como uma string vaziashow()- imprimeDocument: [content]
TextDocument:AppendCommand- recebe um documento e um texto em seu construtor, e anexa o texto quando executadoClearCommand- recebe um documento em seu construtor, e o limpa quando executadoShowCommand- recebe um documento em seu construtor, e mostra seu conteúdo quando executado
TextEditor(o invocador) que armazena uma lista de comandos. Ela deve ter um métodoaddCommand(Command command)para enfileirar comandos, e um métodorunAll()que executa todos os comandos enfileirados em ordem.main.dart: Importe seu arquivo de comandos e demonstre o padrão Command. Crie umTextDocumente umTextEditor. Enfileire os seguintes comandos em ordem: appendHello, appendWorld, 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
}
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
1Fundamentos de POO
Arquivos ExternosBibliotecas e ImportaçõesIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosVariáveis de InstânciaFundamentos de ConstrutoresRecapitulação - Calculadora Simples4Null Safety
Introdução ao Null SafetyNullable vs Non-NullableOs Operadores ? e !Palavra-chave Late e Null SafetyOperadores Null-AwareNull Safety em ClassesRecapitulação - Sistema de Perfil de Usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementando vs EstendendoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Generics
Visão Geral de List, Set e MapColeções Type-SafeClasses GenéricasMétodos GenéricosRestrições de GenericsIterable e IteratorRecapitulação - Armazenamento Genérico13Conceitos Avançados de POO
Composição vs HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário2Construtores em Dart
Construtor PadrãoConstrutores NomeadosListas de InicializaçãoConstrutores ConstantesConstrutores FactoryConstrutores de RedirecionamentoRecapitulação - Shape Builder5Encapsulamento
Membros Públicos vs PrivadosA Convenção do Prefixo _Privacidade em Nível de BibliotecaAprofundamento em Getters & SettersOcultação de InformaçãoRecapitulação - Registros de Estudantes8Mixins
Introdução aos MixinsCriando MixinsUsando Múltiplos MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema Animal11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode & ==Interface ComparableMétodo call()Sobrescrita de noSuchMethodRecapitulação - Coleção Personalizada14Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy3Propriedades de Classe
Membros de Instância vs EstáticosCampos Final & ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Conta Bancária6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodoA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do PolimorfismoPolimorfismo via InterfacesVerificação de Tipos em Tempo de ExecuçãoOs Operadores is & asPalavra-chave CovariantRecapitulação - Processador de Pagamentos12POO Assíncrona
Futures e async/awaitFundamentos de StreamsStream ControllersConstrutores AssíncronosAsync em Métodos de ClasseRecapitulação - Data Fetcher15Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository