Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login
download     post favorito     comentários

Design Patterns – Command

Mais uma vez estou aqui para falar de padrões de projetos, e hoje vamos falar de um padrão que gosta de estar no controle de tudo de ser o “cara” que chama (invoke) todos os comandos, mas ele mesmo não faz a operação, curioso para saber mais sobre esse Design Pattern?]

[fechar]

Você não gostou da qualidade deste conteúdo?

(opcional) Você poderia comentar o que não lhe agradou?

Confirmo meu voto negativo


Mais uma vez estou aqui para falar de padrões de projetos, e hoje vamos falar de um padrão que gosta de estar no controle de tudo de ser o “cara” que chama (invoke) todos os comandos, mas ele mesmo não faz a operação, curioso para saber mais sobre esse Design Pattern?

Vamos lá, o Command permite encapsular um objeto e fornecer a outro uma forma de executar o método do objeto encapsulado de forma dinâmica, o objeto que da inicio a ação é um controle remoto que tem um comando definido em um dos seus slots , responsável pela execução do objeto alvo, o controle remoto, pode ter vários slots e nós podemos configurar cada slot uma operação diferente, e em qualquer momento modificar a ação do slot configurando outra operação, vamos também ver mais um pouco de ligação leve já falado na parte 4 dessa série.

O cenário é o seguinte, vamos automatizar alguns pontos de nossa casa, vamos automatizar a luz e ventilador da sala e também a luz e portão elétrico da garagem, vamos criar um controle com 7 slots cada slot com dois botões, um ON e um OFF.

Começaremos pela Interface para os comandos, relembrando ligações leves, são todas ligações entre objetos onde eles se conhecem muito pouco, só o suficiente para as chamadas, isso permite uma grande flexibilidade na criação de nossas classes para utilização dos nossos recursos, neste caso o controle remoto, onde teremos 3 classes distintas para ser utilizadas no nosso controle.

public class SemComando : ICommand

{

    public void Executar() { }

}

Como podem ver a interface só vai fornecer o Executar do objeto, ou seja, só a start da execução, como e o que o objeto vai fazer não importa, o que importa é que o command vai ser executado pelo controle.

Agora vamos criar a classe responsável pelo controle do todos os comandos.

public class ControleRemoto

{

    private ICommand[] _onComando;

    private ICommand[] _offComando;

 

    public ControleRemoto()

    {

        _onComando = new ICommand[7];

        _offComando = new ICommand[7];

 

        ICommand semComando = new SemComando();

        for (int i = 0; i < 7; i++)

        {

            _onComando[i] = semComando;

            _offComando[i] = semComando;

        }

    }

 

    public void SetaComando(int slot, ICommand onComando, ICommand offComado)

    {

        _onComando[slot] = onComando;

        _offComando[slot] = offComado;

    }

 

    public void OnPressionado(int slot)

    {

        _onComando[slot].Executar();

    }

 

    public void OffPressionado(int slot)

    {

        _offComando[slot].Executar();

    }

 

    public override string ToString()

    {

        StringBuilder sb = new Str…

  [Ver no arquivo exemplo]

        return sb.ToString();

    }

}

É essa a classe para criarmos nosso controle, ela é responsável por armazenar as operações para cada slot e fornecer o método que vai chamar (invoke) o método Executar() de cada botão, e também fornecer uma forma de “setarmos” os comandos nos slots, para isso temos um array do tipo da interface ICommand para ficar a operação do botão ON e um para o botão OFF de cada slot. Uma curiosidade é no construtor estamos “setando” todos os slots com um comando default “SemComando”, vamos dar uma olhada logo abaixo, ele é responsável pela execução do invoke quando pressionado um botão de um slot vazio.

public class SemComando : ICommand

{

    public void Executar() { }

}

Como sempre para termo uma ligação leve e com a possibilidade de varias classes participando dessa ligação, fazemos com que elas assinem uma interface, e como parte do contrato as classes devem implementar todos os métodos, propriedade e etc. da interface. A classe SemComando vai nos fornecer a possibilidade de “setar” um slot que tenha um comando já definido em algum momento para null, ou seja, sem ação.

Agora vamos criar os nossos objetos alvos, serão classes normais que sobreviveriam na aplicação, independentes de outras classes, fazendo as suas operações de forma direta, ou seja, chamando seus métodos de um objeto instanciado da própria classe.

public class Luz

{

    public string _local = string.Empty;

 

    public Luz() { }

 

    public void Ligar()

    {

        Console.WriteLine("{0} luz ligada", _local);

    }

 

    public void Desligar()

    {

        Console.WriteLine("{0} luz desligada", _local);

    }

}

A primeira será a Luz, uma classe bem simples com a função liga e desliga bem simples, essa classe não se ligará direto ao controle, ela será encapsulada em outra classe (Command), mais essa é nossa intenção, permitindo uma centralização das operações. Vamos ver as demais classes.

public class PortaEletronica

{

    public string _local = string.Empty;

 

    public PortaEletronica() { }

 

    public void Abrir()

    {

        Console.WriteLine("{0} porta aberta", _local);

    }

 

    public void Fechar()

    {

        Console.WriteLine("{0} porta fechada", _local);

    }

 

    public void LigarLuz()

    {

        Console.WriteLine("{0} luz ligada", _local);

    }

 

    public void DesligarLuz()

    {

        Console.WriteLine("{0} luz desligada", _local);

    }

 

 

}

 

public class VentiladorTeto

{

    public enum Nivel

    {

        DESLIGADO = 0,

        BAIXO = 1,

        MEDIO = 2,

        ALTO = 3

    }

 

    public string _local = string.Empty;

    public Nivel _nivel = Nivel.DESLIGADO;

 

    public VentiladorTeto() { }

 

    public void High()

    {

        _nivel = Nivel.ALTO;

        Console.WriteLine("{0} ventilado está com velocidade alta", _local);

    }

 

    public void Medium()

    {

        _nivel = Nivel.MEDIO;

        Console.WriteLine("{0} ventilado está com velocidade média", _local);

    }

 

    public void Low()

    {

        _nivel = Nivel.BAIXO;

        Console.WriteLine("{0} ventilado está com velocidade baixa", _local);

    }

 

    public void Off()

    {

        _nivel = Nivel.DESLIGADO;

        Console.WriteLine("{0} ventilado está desligado", _local);

    }

 

    public void Atualizar()

    {

        switch (this._nivel)

        {

            case VentiladorTeto.Nivel.DESLIGADO:

                this.Off();

                break;

            case VentiladorTeto.Nivel.BAIXO:

                this.Low();

                break;

            case VentiladorTeto.Nivel.MEDIO:

                this.Medium();

                break;

            case VentiladorTeto.Nivel.ALTO:

                this.High();

                break;

            default:

                break;

        }

    }

}

Um ponto interessante é que as 3 classes fazem operações diferentes, umas até que semelhantes como a luz e a luz da garagem, mas notem que são classes diferentes e métodos diferentes.

Vocês devem estar pensando, “Que padrão chato” ou “Não vi nada interessante neste padrão”, pois bem, queremos criar uma forma unificada de chamar os métodos das nossas classes, com isso criamos um controle onde ficaram essas chamadas, criamos também uma interface e sabemos que todos os comandos devem assinar a mesma, criamos um único comando simples, e 3 classes distintas e vamos fazer com que nosso controle executa as funções dessas classes quando um botão for pressionado, isso tudo com uma grande flexibilidade.

E aqui está nosso primeiro command, ou melhor, nosso segundo.

//Luz

public class onLuzSala : ICommand

{

    Luz _luz;

    public onLuzSala(Luz luz)

    {

        luz._local = "Sala";

        this._luz = luz;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        this._luz.Ligar();

    }

 

    #endregion

}

E como está no contrato da interface, criamos o método Executar, essa classe recebera um objeto do tipo Luz, vai “setar” o local (_local) e guardar esse objeto em uma variável interna para se lembrar do mesmo quando o seu Executar for chamado pelo controle. Com isso quando invocado o método Executar desse objeto ele vai chamar o método Ligar do objeto passado a ele, as classes command vão encapsular um objeto e fornecer ao controle a forma de chamar o método especifico do objeto. Vamos agora criar o command para desligar essa luz.

public class offLuzSala : ICommand

{

    Luz _luz;

    public offLuzSala(Luz luz)

    {

        luz._local = "Sala";

        this._luz = luz;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        this._luz.Desligar();

    }

 

    #endregion

}

Não podendo ser diferente, essa classe também assina a ICommand, cria o método Executar (único método que o controle conhece) recebe um objeto do tipo Luz (encapsulando) no seu construtor e também invoca outro método quando o Executar() for chamado, só que desta vez ele chama o método desligar. Vamos conferir as outras classes Command.

 

public class onLuzGaragem : ICommand

{

    PortaEletronica _PortaEletronica;

    public onLuzGaragem(PortaEletronica portaEletronica)

    {

        portaEletronica._local = "Garagem";

        this._PortaEletronica = portaEletronica;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        this._PortaEletronica.LigarLuz();

    }

 

    #endregion

}

 

public class offLuzGaragem : ICommand

{

    PortaEletronica _PortaEletronica;

    public offLuzGaragem(PortaEletronica portaEletronica)

    {

        portaEletronica._local = "Garagem";

        this._PortaEletronica = portaEletronica;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        this._PortaEletronica.DesligarLuz();

    }

 

    #endregion

}

 

public class onPortaGaragem : ICommand

{

    PortaEletronica _PortaEletronica;

    public onPortaGaragem(PortaEletronica portaEletronica)

    {

        portaEletronica._local = "Garagem";

        this._PortaEletronica = portaEletronica;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        this._PortaEletronica.Abrir();

    }

 

    #endregion

}

 

public class offPortaGaragem : ICommand

{

    PortaEletronica _PortaEletronica;

    public offPortaGaragem(PortaEletronica portaEletronica)

    {

        portaEletronica._local = "Garagem";

        this._PortaEletronica = portaEletronica;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        this._PortaEletronica.Fechar();

    }

 

    #endregion

}

 

 

public class upVentiladorSala : ICommand

{

    VentiladorTeto _VentiladorTeto;

    public upVentiladorSala(VentiladorTeto ventiladorTeto)

    {

        ventiladorTeto._local = "Sala";

        this._VentiladorTeto = ventiladorTeto;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        if (this._VentiladorTeto._nivel != VentiladorTeto.Nivel.ALTO)

        {

            this._VentiladorTeto._nivel++;

            this._VentiladorTeto.Atualizar();

        }

    }

 

    #endregion

}

 

public class downVentiladorSala : ICommand

{

    VentiladorTeto _VentiladorTeto;

    public downVentiladorSala(VentiladorTeto ventiladorTeto)

    {

        ventiladorTeto._local = "Sala";

        this._VentiladorTeto = ventiladorTeto;

    }

 

    #region ICommand Members

 

    public void Executar()

    {

        if (this._VentiladorTeto._nivel != VentiladorTeto.Nivel.DESLIGADO)

        {

            this._VentiladorTeto._nivel--;

            this._VentiladorTeto.Atualizar();

        }

    }

 

    #endregion

}

Todas são parecidas, assinam a interface, criam o método Executar(), recebe um objeto (encapsula) do tipo a ser invocado, e quando o Executar() é chamado ela faz a sua operação e invoca o método do objeto recebido. Para cada ação das classes encapsuladas (Luz, PortaEletronica e VentiladorTeto) vamos ter que criar um command para ser passado ao nosso controle.

Vamos testar o nosso controle e os nossos comandos.

class TesteCommand

{

    static void Main(string[] args)

    {

        ControleRemoto controle = new ControleRemoto();

 

        Luz luzSala = new Luz();

        PortaEletronica portaoGaragem = new PortaEletronica();

        VentiladorTeto ventiladorSala = new VentiladorTeto();

 

        controle.SetaComando(0, new onLuzSala(luzSala), new offLuzSala(luzSala));

        controle.SetaComando(1, new onLuzGaragem(portaoGaragem), new offLuzGaragem(portaoGaragem));

        controle.SetaComando(2, new onPortaGaragem(portaoGaragem), new offPortaGaragem(portaoGaragem));

        controle.SetaComando(3, new upVentiladorSala(ventiladorSala), new downVentiladorSala(ventiladorSala));

 

        controle.OnPressionado(0);

        controle.OnPressionado(1);

        controle.OnPressionado(2);

        controle.OffPressionado(0);

        controle.OffPressionado(1);

        controle.OffPressionado(2);

        controle.OnPressionado(3);

        controle.OnPressionado(3);

        controle.OnPressionado(3);

        controle.OffPressionado(3);

  controle.OnPressionado(7);

  controle.OffPressionado (7);

  Console.WriteLine(controle);

        Console.ReadKey();

    }

}

Criamos o controle do tipo ControleRemoto, criamos e instanciamos uma Luz, uma PortaEletronica e um VentiladorTeto, e setamos os comando no controle com o SetaComando passando o numero do slot, uma instancia de um comando para o botão ON encapsulando o objeto alvo no mesmo, e uma instancia de um comando para o botão OFF também encapsulando o mesmo objeto passado para o ON. Simulamos vários pressionamento nos botões ON e OFF dos slots, inclusive do slot 7 que está com o comando SemComando, e por fim imprimimos o controle na tela, quando feito isso ele executa o método ToString() do controle (não foi mostrado neste artigo, mas vocês podem conferir no exemplo para download)

Bom, o que fizemos hoje foi simplesmente encapsular a chamado de determinados métodos das nossas classes, permitindo que os mesmos sejam executados através de um controle que nem conhece a classe encapsulada, vocês observaram que o controle remoto pode variar conforme o usuário logado ou uma hora o comando vai estar no slot 1 em outra no slot 4, isso vai depender da sua necessidade, o padrão Command vai ser abordado futuramente com mais recursos, por hora o que já vimos da para entender um pouco o seu poder.

Levantamento:

Vimos novamente ligações leves, uma boa pratica OO para programação, onde os objetos relacionados conhecem muito pouco um sobre o outro.

Também vimos como encapsular um objeto para fornecer a outro objeto meios de executar um determinado método, com certeza esse padrão eleva o encapsulamento para um nível mais alto.

E aprendemos mais um padrão “GoF”, o Command onde podemos criar um controle para nossos objetos de forma dinâmica.

Por hoje é só, espero ter conseguido passar mais esse conceito de forma simples e que vocês estejam gostando, só lembrando que em breve voltarei a falar mais sobre o Command.

Até a próxima.

 

Fontes:

FREEMAN, ERIC & FREEMAN, ELISABETH – Use a Cabeça! Padrões de Projetos (Design Patterns), 2ª Edição [http://www.livrariasaraiva.com.br/produto/1995765/]




Analista de sistemas e analista de processos, desenvolvendo sistemas web, desktop e móbile nas linguagens C#, VB.Net, PHP, ASP e ASP.net. Atuo como gerente de projeto na empresa Techinsys Soluções em TI.

O que você achou deste post?
Conhece a assinatura MVP?
Publicidade
Serviços

Mais posts