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 parametrizar clientes com diferentes solicitações, enfileirar operações ou oferecer suporte à funcionalidade de desfazer. Em vez de chamar um método diretamente, você encapsula a ação em um objeto de comando que pode ser armazenado, transmitido ou executado posteriormente.
O padrão envolve três componentes principais: uma interface Command com um método execute, Comandos Concretos que implementam ações específicas e um Receptor que realiza o trabalho de fato. Um Invocador opcional aciona os 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(); // A luz está LIGADA
remote.setCommand(TurnOffCommand(light));
remote.pressButton(); // A luz está DESLIGADA
}O RemoteControl não sabe nada sobre luzes — ele apenas executa qualquer comando que receba. Esse desacoplamento facilita a adição de 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, nos quais você precisa tratar ações como objetos de primeira classe.
Desafio
FácilVamos criar 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 do padrão Command — pense nele como um sistema de desfazer/refazer, no qual 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 contenha um campoString content(inicialmente vazio) e tenha 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 adiciona o texto quando executadoClearCommand— recebe um documento em seu construtor e o limpa quando executadoShowCommand— recebe um documento em seu construtor e exibe seu conteúdo quando executado
TextEditor(o invocador) que armazene uma lista de comandos. Ela deve ter um métodoaddCommand(Command command)para enfileirar comandos e um métodorunAll()que execute todos os comandos enfileirados na ordem.main.dart: importe seu arquivo de comandos e demonstre o padrão Command. Crie umTextDocumente umTextEditor. Enfileire os seguintes comandos, nesta ordem: adicionarHello, adicionarWorld, exibir o documento, adicionar!, exibir novamente, limpar o documento e exibi-lo uma última vez. Em seguida, execute todos os comandos enfileirados.
Observe como o TextEditor não sabe nada sobre adicionar, limpar ou exibir — ele apenas executa quaisquer comandos que receba. Esse desacoplamento é a essência do padrão Command!
Saída esperada:
Document: Hello World
Document: Hello World!
Document: Experimente você mesmo
import 'commands.dart';
void main() {
// TODO: Crie uma instância de TextDocument
// TODO: Crie uma instância de TextEditor
// TODO: Enfileire os seguintes comandos em ordem:
// 1. Adicione "Hello"
// 2. Adicione " World"
// 3. Mostre o documento
// 4. Adicione "!"
// 5. Mostre o documento novamente
// 6. Limpe o documento
// 7. Mostre o documento uma última vez
// TODO: Execute 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 OOP
Arquivos externosBibliotecas e importsIntrodução à OOPClasses vs. objetosA palavra-chave thisMétodosVariáveis de instânciaNoções básicas de construtoresRevisão - Calculadora simples4Segurança contra nulos
Introdução à segurança contra nulosAnulável vs. não anulávelOs operadores ? e !Palavra-chave Late e segurança contra nulosOperadores conscientes de nulosSegurança contra nulos em classesRecapitulação — Sistema de perfil de usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementação vs. ExtensãoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Genéricos
Visão geral de List, Set e MapColeções com segurança de tiposClasses genéricasMétodos genéricosRestrições genéricasIterable e IteratorRecapitulação - Armazenamento genérico13Conceitos Avançados de OOP
Composição vs. HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Registros (Dart 3)Padrões e Correspondência (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses de Livros e Usuários2Construtores em Dart
Construtor padrãoConstrutores nomeadosListas de inicializaçãoConstrutores constantesConstrutores factoryConstrutores de redirecionamentoRevisão - Construtor de formas5Encapsulamento
Membros Públicos vs. PrivadosConvenção do Prefixo _Privacidade no Nível da BibliotecaAprofundamento em Getters e SettersOcultação de InformaçõesRecapitulação - Registros de Alunos8Mixins
Introdução a MixinsCriando MixinsUsando vários MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema de Animais11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode e ==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 e ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Contas Bancárias6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodosA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do polimorfismoPolimorfismo por meio de interfacesVerificação de tipos em tempo de execuçãoOs operadores is e asPalavra-chave covarianteRecapitulação — Processador de pagamentos12OOP Assíncrona
Futures e async/awaitNoções básicas de StreamsControladores de StreamConstrutores assíncronosAsync em métodos de classeRecapitulação — Buscador de dados15Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão RepositoryPratique por conta própria: Compilador de Dart online