E aí pessoal! Tudo de bem?

Continuando nossa série sobre design patterns, vamos hoje conhecer o pattern Command.

Command

Command faz parte do grupo de padrões comportamentais. Ele tem como responsabilidade encapsular uma solicitação como um objeto, o que lhe permite parametrizar outros objetos com diferentes solicitações, enfileirar ou fazer solicitações e implementar o cancelamento de operações. Isso inclui informações como o nome do método, o objeto que o método pertence e os valores dos parâmetros do método.

Sua estrutura básica é definida pelo UML a seguir:

Diagrama UML da estrutura do padrão command.

Basicamente, nosso cliente instancia um novo ConcreteCommand, que implementa a interface Command, tendo basicamente apenas um método, executar o comando.

A classe Receiver, poderia ser qualquer coisa, um serviço de dados, um serviço de cálculo, enfim, que sabe como executar uma determinada tarefa.

O ContcreteCommand é responsável por fazer o vínculo das informações armazenadas nele com o objeto da classe Receiver. Um Invoker vai invocar o método execute(), que irá realizar as ações necessárias no objeto Receiver().

Ele implementa uma inferface ou classe abstrata para disponibilizar uma forma comum de acionamento para todas as classes concretas que forem criadas implementando esse contrato.

Vamos dar um exemplo para deixar isso mais claro.

Considere um “simples” interruptor. Nesse exemplo vamos configurar o interruptor com duas funções: ligar e desligar a luz.

Um benefício do padrão command é que o interruptor pode ser usado em qualquer dispositivo, não somente uma luz — no próximo exemplo, o Switch liga e desliga a luz, mas o construtor do Switch aceita qualquer subclasse de Command para seus dois parametros. Por exemplo, você pode configurar o Switch (interruptor) para ligar uma televisão ou um aspirador de pó.

Ainda não está claro, certo? Aplicando o padrão command, tornamos a lógica de desligar ou ligar desacoplada de quem precisa solicitar a ação. Vamos ver isso no exemplo a seguir, vendo parte por parte.

Vamos criar uma classe base que represente qualquer dispositivo que pode ser ligado e desligado.

public abstract class Dispositivo
{
    private bool _ligado;
    
    public Dispositivo() => _ligado = false;
    
    public void Ligar() => _ligado = true;
    
    public void Desligar() => _ligado = false;
    
    public string Status
    {
    	get { return _ligado; }
    }
}

Notem que na classe abstrata que representa nossos dispositivos, temos o método ligar e desligar, mas não coloquei nenhuma validação de estado, por enquanto. Agora vamos criar nossos dispositivos.

public class Lampada : Dispositivo
{ }
public class Televisao : Dispositivo
{ }

Agora vamos implementar uma classe cliente para usá-las, ainda sem o padrão Command.

public class Program
{
    static void Main(string[] args)
    {
    	var lampada = new Lampada();
        var televisao = new Televisao();
        
        lampada.Ligar();
        televisao.Ligar();
        
        Console.WriteLine($"Lampada: {lampada.Status}");
        Console.WriteLine($"Televisao: {televisao.Status}");
        
        televisao.Desligar();
        Console.WriteLine($"Televisao: {televisao.Status}");
    }
}

Com o código acima, ligamos e desligamos a lâmpada e a televisão, mas além do código ficar extenso, nosso programa principal precisa conhecer como ligar cada dispositivo. E se tivéssemos uma máquina de lavar, onde antes de ligar, também precisamos abrir o registro? Poderíamos ter um comando responsável por encadear estas ações, por exemplo.

Agora vamos escrever o exemplo acima, utilizando o pattern.

public interface IComando
{
	void Executar();
}

Todos os comandos irão implementar esta interface, tendo como contrato padrão o método Executar(). Cada implementação comando deverá receber os argumentos necessários para execução do comando, mas ele saberá como utilizá-los. Com isso, nosso programa principal apenas deverá saber que um comando tem algumas dependências e deverá ser executado. Simples assim.

public class LigarTelevisaoComando : IComando
{
    private Televisao _televisao;
    
    public LigarTelevisaoComando(Televisao televisao)
    {
    	this._televisao = televisao;
    }
    
    public void Executar()
    {
    	this._televisao.Ligar();
        Console.WriteLine($"Televisão: {this._televisao.Status}");
    }
}
public class DesligarTelevisaoComando : IComando
{
    private Televisao _televisao;
    
    public DesligarTelevisaoComando(Televisao televisao)
    {
    	this._televisao = televisao;
    }
    
    public void Executar()
    {
    	this._televisao.Desligar();
        Console.WriteLine($"Televisão: {this._televisao.Status}");
    }
}
public class LigarLampadaComando : IComando
{
    private Lampada _lampada;
    
    public LigarLampadaComando(Lampada lampada)
    {
    	this._lampada = lampada;
    }
    
    public void Executar()
    {
    	this._lampada.Ligar();
        Console.WriteLine($"Lampada: {this._lampada.Status}");
    }
}
public class DesligarLampadaComando : IComando
{
    private Lampada _lampada;
    
    public DesligarLampadaComando(Lampada lampada)
    {
    	this._lampada = lampada;
    }
    
    public void Executar()
    {
    	this._lampada.Desligar();
        Console.WriteLine($"Lampada: {this._lampada.Status}");
    }
}

Notem que cada comando tem uma responsabilidade, bem definida e visível por seu nome. Mesmo que no caso de uma máquina de lavar, por exemplo, seja necessário abrir a torneira e depois ligar, o comando tem sua finalidade, ligar a máquina, independente do que ele precisa fazer com ela para isso. Fica claro a ação que será tomada e os passos estão dentro desta "receita" para a ação.

Vamos ver agora como fica nosso programa principal, chamando diretamente os comandos. Ainda temos outra forma, mais elegante, que vamos ver depois.

public class Program
{
    static void Main(string[] args)
    {
    	var lampada = new Lampada();
        var televisao = new Televisao();
        
        new LigarTelevisaoComando(televisao).Executar();
        new DesligarTelevisaoComando(televisao).Executar();
        
        new LigarLampada(lampada).Executar();
        new DesligarLampada(lampada).Executar();
    }
}

O código do programa principal ficou bem mais limpo e claro de entender, não ficou? E ainda estamos executando os comandos diretamente em nossa linha de código. Podemos ainda fazer algo semelhante a isso com o exemplo a seguir. É um exemplo bem grosseiro, não estou me preocupando agora com Locks e afins, para entender as possibilidades do Command. Vamos ver algo muito mais elegante quando estivermos abordando o pattern Mediator.

public class ProcessadorDeComandos
{
    private Queue<ICommand> _filaDeComandos;
    private Thread _threadProcessador;
    
    public ProcessadorDeComandos()
    {
    	_filaDeComandos = new Queue<ICommand>();
        _threadProcessador = new Thread(Processar);
        _thread.Start();
    }
    
    public void AdicionarComando(ICommand comando)
    {
    	_filaDeComandos.Enqueue(comando);
    }
    
    private void Processar()
    {
    	while (true)
        {
            foreach (var cmd in _filaDeComandos)
            {
            	cmd.Executar()
            }
            Thread.sleep(5000);
        }
    }
}
public class Program
{
    static void Main(string[] args)
    {
    	var processador = new ProcessadorDeComandos();
    
    	var lampada = new Lampada();
        var televisao = new Televisao();
        
        processador.AdicionarComando(new LigarTelevisao(televisao));
        processador.AdicionarComando(new LigarLampada(lampda));
    }
}

Notem que agora, simplesmente enviamos os comandos para alguém responsável por executar eles, em background.

No processador, fiz de forma simples uma thread com o um Loop, com um Thread.Sleep de 5 segundos, para não ocupar o processador 100% do tempo.

Isto é muito útil quando precisamos trabalhar em nossa UI de forma reativa, recebendo eventos. Imagina que estamos conectados via rede com algum dispositivo e conforme este dispositivo envia informações, diferentes comandos devem ser enfileirados e executados, modificando a UI de acordo com o que recebemos de informação. E nosso programa principal fica apenas com a responsabilidade de gerenciar e enfileirar estes eventos. A execução da ação do comando, independente de quantos passos e classes precise, é responsabilidade dele apenas.

Isto facilita também ao testar nosso código, pois podemos fazer um Mock das dependências externas do comando e testar individualmente cada comando em diferentes cenários, tornando nossos testes mais organizados e fáceis de manter.

Vamos encerrar com a ficha resumo do pattern para organizar melhor todas estas informações.

Ficha Resumo

  • Nome: Command;
  • Objetivo / intenção: O Padrão Command objetivo encapsular uma solicitação como um objeto, o que lhe permite parametrizar outros objetos com diferentes solicitações, enfileirar ou registrar solicitações e implementar recursos de cancelamento de operações. Isso inclui informações como o método, o objeto que o método pertence e até os valores de parâmetros;
  • Motivação: As vezes temos uma série de eventos sendo recebidos ou mesmo precisamos atualizar uma UI operando diferentes classes para uma ação. Com o pattern Command, separamos essa lógica em classes de responsábilidade única por ação, podendo inclusive ter um histórico de execução, podendo desfazer uma ação caso algo esteja errado;
  • Aplicabilidade:  O ponto principal do Command é o uso de uma classe abstrata ou interface, a qual declara um contrato para execução de operações. Na sua forma mais simples, esta interface inclui uma operação abstrata Execute. As subclasses concretas de Command especificam um par receptor-ação através do armazenamento do receptor como uma variável de instância e pela  implementação de Execute para invocar a solicitação. Desta forma, todos os clientes de objetos command tratam cada objeto como uma "caixa preta", simplesmente invocando o método execute(). O Command faz com que seja mais fácil construir componentes que delegam, enfileram ou executam métodos em um momento de sua escolha, sem a necessidade de conhecer a  classe do método ou os parâmetros do método.
  • Estrutura: O diagrama UML deste pattern pode ser dado pela imagem a seguir:
Diagrama UML de exemplo do pattern Command. Alguns elementos ou parâmetros podem variar na implementação.
  • Consequências: Algumas vezes é necessário emitir solicitações para objetos sem saber sobre a operação que está sendo solicitada ou sobre o receptor  da mesma. Com isso precisamos implementa um bom controle de estado de nossa aplicação.
  • Implementações: Abaixo temos um código em C#, onde vamos usar como exemplo a ação de ligar uma máquina de lavar.
public interface IEletrodomestico
{
    void Ligar();
    void Desligar();
}
public class MaquinaLavar : IEletrodomestico
{
    private bool _energia;
    private bool _torneira
    
    public MaquinaLavar()
    {
    	this._energia = false;
        this._torneira = false;
    }
    
    public void Ligar()
    {
    	if (!this._torneira)
            throw new Exception("Abra a torneira antes de ligar")
    	this._energia = true;
    }
    
    public void Desligar()
    {
    	this._energia = false;
    }
    
    public void AbrirTorneira()
    {
    	this._torneira = true;
    }
    
    public void FecharTorneira()
    {
    	if (this._energia)
            throw new Exception("Desligue antes de fechar a torneira");
        this._torneira = false;
    }
}
public interface IComando
{
    void Executar();
}
public class LigarMaquinaComando : IComando
{
    private MaquinaLavar _maquina;
    
    public LigarMaquinaComando(MaquinaLavar maquina)
    {
    	this._maquina = maquina;
    }
    
    public void Executar()
    {
    	this._maquina.AbrirTorneira();
        this._maquina.Ligar();
        Console.WriteLine("Torneira aberta e maquina ligada");
    }
}
public class DesligarMaquinaComando : IComando
{
    private MaquinaLavar _maquina;
    
    public DesligarMaquinaComando(MaquinaLavar maquina)
    {
    	this._maquina = maquina;
    }
    
    public void Executar()
    {
    	this._maquina.Desligar();
        this._maquina.FecharTorneira();
        Console.WriteLine("Maquina desligada e torneira fechada");
    }
}
public class Program
{
    static void main(string[] args)
    {
    	var maquina = new MaquinaLavar();
        
        new LigarMaquinaComando(maquina).Executar();
        
        Thread.Sleep(15000);
        
        new DesligarMaquinaComando(maquina).Executar();
    }
}
  • Usos conhecidos: Muito utilizado em aplicações  que trabalham com máquina de estado, recebendo diversos eventos e tendo que executar ações na UI, por exemplo, refletindo o contexto da máquina de estado após ele ter sido atualizado. Aplicações Android por exemplo, trabalham com este conceito, pois toda atualização da UI é feita através da Thread da UI que recebe comandos a serem executados.
  • Padrões relacionados: Composite, Memento e Prototype

Concluindo

Este foi o padrão Command, com o qual podemos organizar de forma bem mais clara o nosso código, quando precisamos trabalhar com eventos, várias chamadas por ação, e ainda melhorando a testabilidade de nosso código.

Na próxima parte da série iremos abordar o parão Mediator.

Avisos extras

Sábado agora, dia 14, teremos em Alphaville (Barueri) o Azure Weekend, evento gratuito com vários temas sobre Azure e um Workshop sensacional do Azure DevOps. O evento é gratuito e será na Yaman.

Mais detalhes e inscrição nos links a seguir:

Azure Weekend @ Yaman
Evento sobre Azure na Yaman
Azure Weekend 2019 - Alphaville-SP [Presencial|Gratuito]
Você quer conhecer mais sobre computação em nuvem e como funcionam os serviços do Microsoft Azure? Então não perca a 1a. edição do Azure Weekend em Alphaville-SP, um EVENTO PRESENCIAL e GRATUITO promovido pelo .NET SP, DevOps Professionals, Azure Talks, SampaDevs, Open Source SP, Aprendendo JS e Cam…

Por hoje é isso pessoal, um abraço a todos e até a próxima.