Implementando Controles Remotos Flexíveis com o Padrão Command

O Padrão Command (Comando) é um padrão de projeto comportamental que encapsula uma solicitação como um objeto, permitindo que você parametrize clientes com diferentes solicitações, filas de solicitações ou logs de solicitações. Ele também oferece suporte para operações desfazíveis (undo). O uso principal do Command é desacoplar o objeto que invoca a operação do objeto que a executa. Ao encapsular chamadas de método, podemos transformar blocos de código em objetos reutilizáveis e rastreáveis, sem que o invocador precise saber como a operação é realizada.

Vamos explorar esse padrão com um exemplo prático: um sistema de controle remoto que precisa operar diversos dispositivos.

I. Controle Remoto Simples

Começaremos com um controle remoto básico que possui um único botão e um slot para um dispositivo.

1. Modelando um Dispositivo: Lâmpada

Primeiro, definimos uma classe para um dispositivo simples, como uma lâmpada, com métodos para ligar e desligar.

using System;

namespace PadraoCommand.Dispositivos
{
    public class Lampada
    {
        private string _localizacao;

        public Lampada(string localizacao)
        {
            _localizacao = localizacao;
        }

        public void Ligar()
        {
            Console.WriteLine($"{_localizacao}: Lâmpada ligada.");
        }

        public void Desligar()
        {
            Console.WriteLine($"{_localizacao}: Lâmpada desligada.");
        }
    }
}

2. Interface para Comandos

Para encapsular as ações, criamos uma interface de comando com um único método para execução.

namespace PadraoCommand.Comandos
{
    public interface IComando
    {
        void Executar();
    }
}

3. Comando Específico para Ligar a Lâmpada

Agora, implementamos um comando concreto que liga uma lâmpada específica. Este comando recebe uma instância de Lampada e, ao ser executado, chama o método Ligar() da lâmpada.

namespace PadraoCommand.Comandos
{
    public class ComandoLigarLampada : IComando
    {
        private Lampada _lampadaAlvo;

        public ComandoLigarLampada(Lampada lampada)
        {
            _lampadaAlvo = lampada;
        }

        public void Executar()
        {
            _lampadaAlvo.Ligar();
        }
    }
}

4. O Controle Remoto: Invocador

Criamos um controle remoto simples que possui um slot para um comando. Ele não sabe qual comando está no slot, apenas que pode executá-lo.

namespace PadraoCommand.Controles
{
    public class ControleRemotoSimples
    {
        private IComando _slotComando;

        public ControleRemotoSimples() { }

        public void AtribuirComando(IComando comando)
        {
            _slotComando = comando;
        }

        public void PressionarBotao()
        {
            _slotComando.Executar();
        }
    }
}

5. Testando o Controle Remoto Simples

Para testar, configuramos o controle remoto com o comando para ligar a lâmpada e pressionamos o botão.

using System;
using PadraoCommand.Comandos;
using PadraoCommand.Controles;
using PadraoCommand.Dispositivos;

namespace PadraoCommand.Testes
{
    class ProgramaPrincipal
    {
        static void Main(string[] args)
        {
            Console.WriteLine("--- Teste do Controle Remoto Simples ---");

            ControleRemotoSimples controleSimples = new ControleRemotoSimples();
            Lampada lampadaSala = new Lampada("Sala de Estar");
            ComandoLigarLampada ligarLampada = new ComandoLigarLampada(lampadaSala);

            controleSimples.AtribuirComando(ligarLampada);
            controleSimples.PressionarBotao(); // Ligar a lâmpada

            Console.WriteLine("\n--- Adicionando outro dispositivo ---");
            PortaoGaragem portao = new PortaoGaragem("Garagem");
            ComandoAbrirPortaoGaragem abrirPortao = new ComandoAbrirPortaoGaragem(portao);
            
            controleSimples.AtribuirComando(abrirPortao);
            controleSimples.PressionarBotao(); // Abrir o portão
        }
    }
}

6. Outro Dispositivo: Portão da Garagem

Para demonstrar a flexibilidade, criamos uma classe para um portão de garagem.

using System;

namespace PadraoCommand.Dispositivos
{
    public class PortaoGaragem
    {
        private string _localizacao;

        public PortaoGaragem(string localizacao)
        {
            _localizacao = localizacao;
        }

        public void Abrir()
        { Console.WriteLine($"{_localizacao}: Portão da garagem está aberto."); }

        public void Fechar()
        { Console.WriteLine($"{_localizacao}: Portão da garagem está fechado."); }

        public void Parar()
        { Console.WriteLine($"{_localizacao}: Portão da garagem parado."); }

        public void LigarLuz()
        { Console.WriteLine($"{_localizacao}: Luz do portão da garagem ligada."); }

        public void DesligarLuz()
        { Console.WriteLine($"{_localizacao}: Luz do portão da garagem desligada."); }
    }
}

7. Comando para Abrir o Portão da Garagem

namespace PadraoCommand.Comandos
{
    public class ComandoAbrirPortaoGaragem : IComando
    {
        private PortaoGaragem _portao;

        public ComandoAbrirPortaoGaragem(PortaoGaragem portao)
        {
            _portao = portao;
        }

        public void Executar()
        {
            _portao.Abrir();
        }
    }
}

II. Controle Remoto Avançado

Um controle remoto real precisa de múltiplos botões e slots. Vamos criar uma versão mais completa que gerencia vários comandos de ligar e desligar.

1. O Invocador: Central de Controle Remoto

Este controle remoto terá slots para comandos de "ligar" e "desligar". Implementamos um ComandoNulo para preencher slots vazios, evitando verificações de nulo.

using System.Text;
using PadraoCommand.Comandos; // Certifique-se de que o namespace para IComando e ComandoNulo esteja acessível

namespace PadraoCommand.Controles
{
    public class CentralControleRemoto
    {
        private IComando[] _botoesLigar;
        private IComando[] _botoesDesligar;

        public CentralControleRemoto()
        {
            const int numeroSlots = 7;
            _botoesLigar = new IComando[numeroSlots];
            _botoesDesligar = new IComando[numeroSlots];

            IComando comandoNulo = new ComandoNulo(); // Padrão Null Object
            for (int i = 0; i < numeroSlots; i++)
            {
                _botoesLigar[i] = comandoNulo;
                _botoesDesligar[i] = comandoNulo;
            }
        }

        public void ConfigurarSlot(int slot, IComando comandoLigar, IComando comandoDesligar)
        {
            _botoesLigar[slot] = comandoLigar;
            _botoesDesligar[slot] = comandoDesligar;
        }

        public void PressionarBotaoLigar(int slot)
        {
            _botoesLigar[slot].Executar();
        }

        public void PressionarBotaoDesligar(int slot)
        {
            _botoesDesligar[slot].Executar();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("\n----- Central de Controle Remoto Avançada -----");
            for (int i = 0; i < _botoesLigar.Length; i++)
            {
                sb.AppendFormat("[Slot {0}] Ligar: {1}, Desligar: {2}\n",
                                i,
                                _botoesLigar[i].GetType().Name,
                                _botoesDesligar[i].GetType().Name);
            }
            return sb.ToString();
        }
    }
}

2. Implementando o Comando Nulo

namespace PadraoCommand.Comandos
{
    public class ComandoNulo : IComando
    {
        public void Executar()
        {
            // Não faz nada, é um objeto nulo para evitar verificações de null.
        }
    }
}

3. Mais Comandos: Desligar Lâmpada e Fechar Portão

Precisamos de comandos para desligar a lâmpada e fechar o portão.

namespace PadraoCommand.Comandos
{
    public class ComandoDesligarLampada : IComando
    {
        private Lampada _lampadaAlvo;

        public ComandoDesligarLampada(Lampada lampada)
        {
            _lampadaAlvo = lampada;
        }

        public void Executar()
        {
            _lampadaAlvo.Desligar();
        }
    }
}

namespace PadraoCommand.Comandos
{
    public class ComandoFecharPortaoGaragem : IComando
    {
        private PortaoGaragem _portao;

        public ComandoFecharPortaoGaragem(PortaoGaragem portao)
        {
            _portao = portao;
        }

        public void Executar()
        {
            _portao.Fechar();
        }
    }
}

4. Testando o Controle Remoto Avançado

using System;
using PadraoCommand.Comandos;
using PadraoCommand.Controles;
using PadraoCommand.Dispositivos;

namespace PadraoCommand.Testes
{
    partial class ProgramaPrincipal
    {
        static void Main(string[] args)
        {
            // ... (código anterior para o controle simples) ...

            Console.WriteLine("\n--- Teste do Controle Remoto Avançado ---");

            CentralControleRemoto central = new CentralControleRemoto();
            Lampada lampadaCozinha = new Lampada("Cozinha");
            PortaoGaragem portaoPrincipal = new PortaoGaragem("Principal");

            ComandoLigarLampada ligarCozinha = new ComandoLigarLampada(lampadaCozinha);
            ComandoDesligarLampada desligarCozinha = new ComandoDesligarLampada(lampadaCozinha);
            ComandoAbrirPortaoGaragem abrirPortao = new ComandoAbrirPortaoGaragem(portaoPrincipal);
            ComandoFecharPortaoGaragem fecharPortao = new ComandoFecharPortaoGaragem(portaoPrincipal);

            central.ConfigurarSlot(0, ligarCozinha, desligarCozinha);
            central.ConfigurarSlot(1, abrirPortao, fecharPortao);

            Console.WriteLine(central.ToString());

            Console.WriteLine("\nPressionando botões do slot 0 (Lâmpada Cozinha):");
            central.PressionarBotaoLigar(0);
            central.PressionarBotaoDesligar(0);

            Console.WriteLine("\nPressionando botões do slot 1 (Portão Garagem):");
            central.PressionarBotaoLigar(1);
            central.PressionarBotaoDesligar(1);
        }
    }
}

III. Implementando Funcionalidade de Desfazer (Undo)

Um dos recursos poderosos do Padrão Command é o suporte a operações de desfazer. Para isso, cada comando precisará de um método para reverter sua ação.

1. Estendendo a Interface IComando

Adicionamos o método Desfazer() à interface de comando.

namespace PadraoCommand.Comandos
{
    public interface IComando
    {
        void Executar();
        void Desfazer(); // Novo método para desfazer a operação
    }
}

Todos os comandos exsitentes precisarão implementar Desfazer(). Para ComandoNulo, será um método vazio.

namespace PadraoCommand.Comandos
{
    public class ComandoNulo : IComando
    {
        public void Executar() { /* Não faz nada */ }
        public void Desfazer() { /* Não faz nada */ }
    }
}

2. Atualizando Comandos para Suportar Desfazer

Para a lâmpada, desfazer "ligar" significa "desligar", e vice-versa.

namespace PadraoCommand.Comandos
{
    public class ComandoLigarLampada : IComando
    {
        private Lampada _lampadaAlvo;
        public ComandoLigarLampada(Lampada lampada) { _lampadaAlvo = lampada; }
        public void Executar() { _lampadaAlvo.Ligar(); }
        public void Desfazer() { _lampadaAlvo.Desligar(); }
    }

    public class ComandoDesligarLampada : IComando
    {
        private Lampada _lampadaAlvo;
        public ComandoDesligarLampada(Lampada lampada) { _lampadaAlvo = lampada; }
        public void Executar() { _lampadaAlvo.Desligar(); }
        public void Desfazer() { _lampadaAlvo.Ligar(); }
    }
}

Da mesma forma para o portão da garagem:

namespace PadraoCommand.Comandos
{
    public class ComandoAbrirPortaoGaragem : IComando
    {
        private PortaoGaragem _portao;
        public ComandoAbrirPortaoGaragem(PortaoGaragem portao) { _portao = portao; }
        public void Executar() { _portao.Abrir(); }
        public void Desfazer() { _portao.Fechar(); }
    }

    public class ComandoFecharPortaoGaragem : IComando
    {
        private PortaoGaragem _portao;
        public ComandoFecharPortaoGaragem(PortaoGaragem portao) { _portao = portao; }
        public void Executar() { _portao.Fechar(); }
        public void Desfazer() { _portao.Abrir(); }
    }
}

3. Controle Remoto com Botão de Desfazer

A central de controle remoto precisa rastrear o último comando executado para poder desfazê-lo.

using System.Text;
using PadraoCommand.Comandos;

namespace PadraoCommand.Controles
{
    public class CentralControleComDesfazer
    {
        private IComando[] _botoesLigar;
        private IComando[] _botoesDesligar;
        private IComando _ultimoComandoExecutado; // Armazena o último comando para desfazer

        public CentralControleComDesfazer()
        {
            const int numeroSlots = 7;
            _botoesLigar = new IComando[numeroSlots];
            _botoesDesligar = new IComando[numeroSlots];

            IComando comandoNulo = new ComandoNulo();
            for (int i = 0; i < numeroSlots; i++)
            {
                _botoesLigar[i] = comandoNulo;
                _botoesDesligar[i] = comandoNulo;
            }
            _ultimoComandoExecutado = comandoNulo; // Inicializa com comando nulo
        }

        public void ConfigurarSlot(int slot, IComando comandoLigar, IComando comandoDesligar)
        {
            _botoesLigar[slot] = comandoLigar;
            _botoesDesligar[slot] = comandoDesligar;
        }

        public void PressionarBotaoLigar(int slot)
        {
            _botoesLigar[slot].Executar();
            _ultimoComandoExecutado = _botoesLigar[slot]; // Armazena para desfazer
        }

        public void PressionarBotaoDesligar(int slot)
        {
            _botoesDesligar[slot].Executar();
            _ultimoComandoExecutado = _botoesDesligar[slot]; // Armazena para desfazer
        }

        public void PressionarBotaoDesfazer()
        {
            Console.WriteLine("Pressionado botão DESFAZER.");
            _ultimoComandoExecutado.Desfazer();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("\n----- Central de Controle com Desfazer -----");
            for (int i = 0; i < _botoesLigar.Length; i++)
            {
                sb.AppendFormat("[Slot {0}] Ligar: {1}, Desligar: {2}\n",
                                i,
                                _botoesLigar[i].GetType().Name,
                                _botoesDesligar[i].GetType().Name);
            }
            sb.AppendLine($"[Último Comando Executado]: {_ultimoComandoExecutado.GetType().Name}");
            return sb.ToString();
        }
    }
}

4. Testando a Funcionalidade de Desfazer

using System;
using PadraoCommand.Comandos;
using PadraoCommand.Controles;
using PadraoCommand.Dispositivos;

namespace PadraoCommand.Testes
{
    partial class ProgramaPrincipal
    {
        static void Main(string[] args)
        {
            // ... (código anterior) ...

            Console.WriteLine("\n--- Teste da Central de Controle com Desfazer ---");

            CentralControleComDesfazer controleComUndo = new CentralControleComDesfazer();
            Lampada lampadaQuarto = new Lampada("Quarto");
            ComandoLigarLampada ligarQuarto = new ComandoLigarLampada(lampadaQuarto);
            ComandoDesligarLampada desligarQuarto = new ComandoDesligarLampada(lampadaQuarto);

            controleComUndo.ConfigurarSlot(0, ligarQuarto, desligarQuarto);

            Console.WriteLine(controleComUndo.ToString());

            Console.WriteLine("\nPressionando LIGAR no Slot 0:");
            controleComUndo.PressionarBotaoLigar(0);
            controleComUndo.PressionarBotaoDesfazer(); // Desfazendo Ligar -> Desliga

            Console.WriteLine("\nPressionando DESLIGAR no Slot 0:");
            controleComUndo.PressionarBotaoDesligar(0);
            controleComUndo.PressionarBotaoDesfazer(); // Desfazendo Desligar -> Liga

            Console.WriteLine(controleComUndo.ToString());
        }
    }
}

IV. Desfazer Complexo: Gerenciando Estado

Para operações mais complexas, o método Desfazer() precisa restaurar um estado prévio. O ventilador de teto é um bom exemplo, pois tem várias velocidades.

1. Dispositivo: Ventilador de Teto

using System;

namespace PadraoCommand.Dispositivos
{
    public class VentiladorTeto
    {
        public const int VELOCIDADE_ALTA = 3;
        public const int VELOCIDADE_MEDIA = 2;
        public const int VELOCIDADE_BAIXA = 1;
        public const int DESLIGADO = 0;

        private string _localizacao;
        private int _velocidadeAtual;

        public VentiladorTeto(string localizacao)
        {
            _localizacao = localizacao;
            _velocidadeAtual = DESLIGADO;
            Console.WriteLine($"Ventilador de teto em {_localizacao} criado.");
        }

        public void AjustarVelocidadeAlta()
        {
            _velocidadeAtual = VELOCIDADE_ALTA;
            Console.WriteLine($"{_localizacao}: Ventilador de teto ajustado para ALTA.");
        }
        public void AjustarVelocidadeMedia()
        {
            _velocidadeAtual = VELOCIDADE_MEDIA;
            Console.WriteLine($"{_localizacao}: Ventilador de teto ajustado para MÉDIA.");
        }
        public void AjustarVelocidadeBaixa()
        {
            _velocidadeAtual = VELOCIDADE_BAIXA;
            Console.WriteLine($"{_localizacao}: Ventilador de teto ajustado para BAIXA.");
        }
        public void Desligar()
        {
            _velocidadeAtual = DESLIGADO;
            Console.WriteLine($"{_localizacao}: Ventilador de teto DESLIGADO.");
        }

        public int ObterVelocidade()
        {
            return _velocidadeAtual;
        }
    }
}

2. Comandos do Ventilador de Teto com Desfazer Inteligente

Os comandos do ventilador devem salvar a velocidade anterior antes de mudar. O método Desfazer() então restaura essa velocidade.

using PadraoCommand.Dispositivos;

namespace PadraoCommand.Comandos
{
    public class ComandoVentiladorTetoVelocidadeAlta : IComando
    {
        private VentiladorTeto _ventilador;
        private int _velocidadeAnterior; // Estado para desfazer

        public ComandoVentiladorTetoVelocidadeAlta(VentiladorTeto ventilador)
        {
            _ventilador = ventilador;
        }

        public void Executar()
        {
            _velocidadeAnterior = _ventilador.ObterVelocidade(); // Salva estado antes da ação
            _ventilador.AjustarVelocidadeAlta();
        }

        public void Desfazer()
        {
            RestaurarVelocidade(_velocidadeAnterior);
        }

        private void RestaurarVelocidade(int velocidade)
        {
            if (velocidade == VentiladorTeto.VELOCIDADE_ALTA) _ventilador.AjustarVelocidadeAlta();
            else if (velocidade == VentiladorTeto.VELOCIDADE_MEDIA) _ventilador.AjustarVelocidadeMedia();
            else if (velocidade == VentiladorTeto.VELOCIDADE_BAIXA) _ventilador.AjustarVelocidadeBaixa();
            else if (velocidade == VentiladorTeto.DESLIGADO) _ventilador.Desligar();
        }
    }

    public class ComandoVentiladorTetoVelocidadeMedia : IComando
    {
        private VentiladorTeto _ventilador;
        private int _velocidadeAnterior;

        public ComandoVentiladorTetoVelocidadeMedia(VentiladorTeto ventilador)
        {
            _ventilador = ventilador;
        }

        public void Executar()
        {
            _velocidadeAnterior = _ventilador.ObterVelocidade();
            _ventilador.AjustarVelocidadeMedia();
        }

        public void Desfazer()
        {
            RestaurarVelocidade(_velocidadeAnterior);
        }

        private void RestaurarVelocidade(int velocidade)
        {
            if (velocidade == VentiladorTeto.VELOCIDADE_ALTA) _ventilador.AjustarVelocidadeAlta();
            else if (velocidade == VentiladorTeto.VELOCIDADE_MEDIA) _ventilador.AjustarVelocidadeMedia();
            else if (velocidade == VentiladorTeto.VELOCIDADE_BAIXA) _ventilador.AjustarVelocidadeBaixa();
            else if (velocidade == VentiladorTeto.DESLIGADO) _ventilador.Desligar();
        }
    }
    
    public class ComandoVentiladorTetoDesligar : IComando
    {
        private VentiladorTeto _ventilador;
        private int _velocidadeAnterior;

        public ComandoVentiladorTetoDesligar(VentiladorTeto ventilador)
        {
            _ventilador = ventilador;
        }

        public void Executar()
        {
            _velocidadeAnterior = _ventilador.ObterVelocidade();
            _ventilador.Desligar();
        }

        public void Desfazer()
        {
            if (_velocidadeAnterior == VentiladorTeto.VELOCIDADE_ALTA) _ventilador.AjustarVelocidadeAlta();
            else if (_velocidadeAnterior == VentiladorTeto.VELOCIDADE_MEDIA) _ventilador.AjustarVelocidadeMedia();
            else if (_velocidadeAnterior == VentiladorTeto.VELOCIDADE_BAIXA) _ventilador.AjustarVelocidadeBaixa();
            else if (_velocidadeAnterior == VentiladorTeto.DESLIGADO) _ventilador.Desligar();
        }
    }
}

3. Testando o Ventilador de Teto com Desfazer

using System;
using PadraoCommand.Comandos;
using PadraoCommand.Controles;
using PadraoCommand.Dispositivos;

namespace PadraoCommand.Testes
{
    partial class ProgramaPrincipal
    {
        static void Main(string[] args)
        {
            // ... (código anterior) ...

            Console.WriteLine("\n--- Teste do Ventilador de Teto com Desfazer ---");
            CentralControleComDesfazer controleVentilador = new CentralControleComDesfazer();
            VentiladorTeto ventiladorSala = new VentiladorTeto("Sala Principal");

            ComandoVentiladorTetoVelocidadeAlta ligarVentiladorAlto = new ComandoVentiladorTetoVelocidadeAlta(ventiladorSala);
            ComandoVentiladorTetoVelocidadeMedia ligarVentiladorMedio = new ComandoVentiladorTetoVelocidadeMedia(ventiladorSala);
            ComandoVentiladorTetoDesligar desligarVentilador = new ComandoVentiladorTetoDesligar(ventiladorSala);

            controleVentilador.ConfigurarSlot(0, ligarVentiladorAlto, desligarVentilador);
            controleVentilador.ConfigurarSlot(1, ligarVentiladorMedio, desligarVentilador);

            Console.WriteLine("\nSlot 0: Ligar ALTA -> Desligar -> Desfazer (Volta para ALTA)");
            controleVentilador.PressionarBotaoLigar(0); // Liga ALTA
            controleVentilador.PressionarBotaoDesligar(0); // Desliga
            Console.WriteLine(controleVentilador.ToString());
            controleVentilador.PressionarBotaoDesfazer(); // Desfaz Desligar -> Volta para ALTA

            Console.WriteLine("\nSlot 1: Ligar MÉDIA -> Ligar ALTA (Slot 0) -> Desfazer (Volta para MÉDIA)");
            controleVentilador.PressionarBotaoLigar(1); // Liga MÉDIA
            Console.WriteLine(controleVentilador.ToString());
            controleVentilador.PressionarBotaoLigar(0); // Liga ALTA (último comando agora é ALTA)
            Console.WriteLine(controleVentilador.ToString());
            controleVentilador.PressionarBotaoDesfazer(); // Desfaz ALTA -> Volta para MÉDIA
        }
    }
}

V. Macros de Comando (Modo Festa)

E se quisermos que um único botão execute várias ações, como ligar todas as luzes, abrir o portão e ajustar o ventilaodr? Podemos criar um ComandoMacro.

1. Implementando o Comando Macro

Um ComandoMacro é um comando que armazena uma coleção de outros comandos e os executa em sequência. Para desfazer, ele executa os comandos em ordem inversa.

using System.Collections.Generic;

namespace PadraoCommand.Comandos
{
    public class ComandoMacro : IComando
    {
        private IComando[] _listaComandos; // Array de comandos a serem executados

        public ComandoMacro(IComando[] comandos)
        {
            _listaComandos = comandos;
        }

        public void Executar()
        {
            foreach (var comando in _listaComandos)
            {
                comando.Executar();
            }
        }

        public void Desfazer()
        {
            // Desfaz na ordem inversa da execução
            for (int i = _listaComandos.Length - 1; i >= 0; i--)
            {
                _listaComandos[i].Desfazer();
            }
        }
    }
}

2. Utilizando Comandos Macro

Criamos arrays de comandos para "ligar festa" e "desligar festa" e os encapsulamos em ComandoMacro.

using System;
using PadraoCommand.Comandos;
using PadraoCommand.Controles;
using PadraoCommand.Dispositivos;

namespace PadraoCommand.Testes
{
    partial class ProgramaPrincipal
    {
        static void Main(string[] args)
        {
            // ... (código anterior) ...

            Console.WriteLine("\n--- Teste do Modo Festa com Comandos Macro ---");

            CentralControleComDesfazer controleFesta = new CentralControleComDesfazer();

            Lampada lampadaSala = new Lampada("Sala de Estar");
            Lampada lampadaCozinha = new Lampada("Cozinha");
            PortaoGaragem portaoPrincipal = new PortaoGaragem("Principal");
            VentiladorTeto ventiladorSala = new VentiladorTeto("Sala Principal");

            // Comandos individuais para ligar/desligar
            ComandoLigarLampada ligarSala = new ComandoLigarLampada(lampadaSala);
            ComandoDesligarLampada desligarSala = new ComandoDesligarLampada(lampadaSala);
            ComandoLigarLampada ligarCozinha = new ComandoLigarLampada(lampadaCozinha);
            ComandoDesligarLampada desligarCozinha = new ComandoDesligarLampada(lampadaCozinha);
            ComandoAbrirPortaoGaragem abrirPortao = new ComandoAbrirPortaoGaragem(portaoPrincipal);
            ComandoFecharPortaoGaragem fecharPortao = new ComandoFecharPortaoGaragem(portaoPrincipal);
            ComandoVentiladorTetoVelocidadeAlta ventiladorAlto = new ComandoVentiladorTetoVelocidadeAlta(ventiladorSala);
            ComandoVentiladorTetoDesligar ventiladorOff = new ComandoVentiladorTetoDesligar(ventiladorSala);

            // Criando arrays para os macros
            IComando[] comandosLigarFesta = { ligarSala, ligarCozinha, abrirPortao, ventiladorAlto };
            IComando[] comandosDesligarFesta = { desligarSala, desligarCozinha, fecharPortao, ventiladorOff };

            // Encapsulando em ComandoMacro
            ComandoMacro macroLigarFesta = new ComandoMacro(comandosLigarFesta);
            ComandoMacro macroDesligarFesta = new ComandoMacro(comandosDesligarFesta);

            controleFesta.ConfigurarSlot(0, macroLigarFesta, macroDesligarFesta);

            Console.WriteLine(controleFesta.ToString());

            Console.WriteLine("\n--- Pressionando botão LIGAR FESTA ---");
            controleFesta.PressionarBotaoLigar(0);

            Console.WriteLine("\n--- Pressionando botão DESLIGAR FESTA ---");
            controleFesta.PressionarBotaoDesligar(0);
            
            Console.WriteLine("\n--- Pressionando botão DESFAZER ---");
            controleFesta.PressionarBotaoDesfazer(); // Desfaz o desligar festa, volta ao estado ligado
        }
    }
}

VI. Aplicações Adicionais do Padrão Command

1. Requisições em Fila

O Padrão Command é ideal para sistemas onde as requisições precisam ser enfileiradas e processadas por um conjunto de trabalhadores (threads). Cada comando é retirado da fila, executado, e descartado após a conclusão. Isso desacopla o produtor de requisições do consumidor, permitindo diferentes estratégias de processamento e gerenciamento de carga.

2. Logging e Transações

Ao registrar todos os comandos executados, um sistema pode recriar seu estado anterior após uma falha ou reiniciar. Se os comandos implementarem um método Desfazer(), também é possível implementar sistemas de transação complexos, onde um conjunto de operações pode ser revertido se ocorrer um erro em qualquer ponto.

VII. Pontos Chave do Padrão Command

  1. O Padrão Command desacopla o objeto que inicia uma requisição (invocador) do objeto que realmente sabe como executá-la (receptor).
  2. A comunicação entre invocador e receptor é feita através de um objeto de comando, que encapsula o receptor e a ação a ser executada.
  3. O invocador aciona a execução do comando chamando seu método Executar(), que por sua vez delega a ação ao receptor.
  4. Comandos podem ser passados como parâmetros e modificados dinamicamente em tempo de execução.
  5. A funcionalidade de desfazer é facilmente implementável ao adicionar um método Desfazer() aos comandos, que reverte as ações do Executar().
  6. Comandos Macro são uma extensão simples, permitnido a execução de múltiplos comandos de uma vez, e também podem suportar desfazer.
  7. Em alguns casos, comandos "inteligentes" podem implementar a lógica da requisição diretamente, sem delegar a um receptor. Embora isso possa simplificar alguns cenários, geralmente reduz o nível de desacoplamento.
  8. O padrão Command é útil para implementar sistemas de fila, logging e transações.

Tags: C# DesignPatterns CommandPattern SoftwareArchitecture UndoRedo

Publicado em 7-26 17:37