[Delphi] Design Patterns GoF – Command

Olá, leitores! Já estamos no 14º artigo sobre Design Patterns!
Dentre todos os padrões de projetos já abordados até o momento, o Command, que será visto neste artigo, foi um dos mais custosos para compreender. Não pelo propósito, mas pela arquitetura de classes que são exigidas e também pela empregabilidade em um ambiente real. Procurei ser o mais objetivo possível para evitar que o artigo ficasse muito extenso. Check it out!

Introdução

Um fato bem interessante que observei ao estudar os padrões de projeto comportamentais é a possibilidade de “personalizar” as ações dos objetos. No artigo anterior, sobre o Chain of Responsibility, por exemplo, nos é concedida a liberdade de compor uma cadeia de responsabilidades por qualquer objeto e em qualquer ordem. Neste artigo você também irá notar essa mesma natureza de customização de ações.

O objeto principal do Command, em poucas palavras, é encapsular requisições como objetos, possibilitando a criação de filas de comandos a serem executados sob determinada ordem. Logo, podemos “empilhar” vários comandos e executá-los de uma só vez somente quando necessário.

Analogia

Para introduzir o conceito do Command, considere um wizard de instalação de um programa, como o da imagem abaixo:

Exemplo de Wizard de Instalação de Programas

Para muitos, esses instaladores são popularmente conhecidos como “Next, Next, Next, Finish”. 😛

Pois bem, através destes wizards, sabemos que é possível personalizar a instalação do programa, selecionando as permissões, pasta de cópia dos arquivos, recursos que serão instalados e criação de atalhos, certo? Porém, lembre-se que a instalação, de fato, só ocorre quando o usuário pressiona o botão “Finalizar” (ou Finish). Isso significa que o wizard “empilha” os comandos (que são as opções de instalação) e somente os executa sob a ação do botão “Finalizar”.

Bom, de forma bem simples, estamos basicamente falando de uma aplicação tradicional do padrão de projeto Command. Esse procedimento poderia ser traduzido no código abaixo:

Utilizar o Command é o mesmo que dizer: “Tenho várias operações preparadas, mas elas só serão executados sob meu comando!” . Interessante, não?

Embora o conceito esteja um pouco mais claro, ainda não paramos por aqui. A teoria do padrão de projeto estabelece que os comandos, por si só, não executam nada. Cada comando deve estar associado a um objeto que, por fim, possui o conhecimento de como executar a operação.  Portanto, existe mais um elemento nesse contexto, comentado logo a seguir.

Considere a seguinte analogia: trabalhamos em um setor de processamento de dados e devemos realizar todo o trâmite de emissão de nota fiscal quando um pedido de venda é recebido. Este pode ser um bom cenário para a aplicação do Command:

Cada comando recebe a instância do objeto que utilizaremos na execução de nossas tarefas. Este objeto poderia, por exemplo, ser substituído por uma ordem de serviço, mas os comandos continuariam os mesmos. Do mesmo modo, é possível também adicionar novos comandos, como exportar a nota fiscal para um formato específico. Por esse motivo que mencionei no primeiro parágrafo que padrões dessa família nos permitem configurar ações.

Por que os comandos precisam estar atrelados a um objeto?
Os comandos atuam como “conexões” entre o cliente e as ações desejadas do objeto. Essa definição nos leva a um alto índice de desacoplamento entre classes, já que os comandos não conhecem os detalhes de implementação das ações que serão executadas. Os comandos, então, apenas recebem uma instância do objeto que possui as implementações e chamam um de seus métodos.

Na analogia acima, TPedido, que é a classe que contém as implementações, é chamada de Receiver. Os comandos (lançar movimento do caixa, emitir nota fiscal e enviar para a contabilidade) são classes que implementam uma Interface chamada Command e são definidas como Concrete Commands. A classe TProcessamento, que armazena a lista de comandos, recebe o nome de Invoker.

Exemplo de codificação do Command

A próximo parte do artigo é apresentar uma codificação prática do Command, no entanto, o exemplo não será um wizard de instalação e nem uma aplicação para processamento de pedidos.

Codificaremos um utilitário para extração de informações do computador, no qual pode ser útil para algumas finalidades. Por exemplo, imagine que uma equipe de suporte de uma empresa de software esteja recebendo vários chamados referentes à falhas em algumas rotinas do sistema. Para averiguar a causa, o analista de suporte poderá executar essa aplicação e extrair a lista de processos em execução, programas instalados e as variáveis de ambiente do computador do cliente. Chique, hein?

Essa aplicação terá a seguinte interface visual:Formulário de exemplo de utilização do Design Pattern Command

O botão “Executar Comandos” irá empilhar a lista de comandos para executá-los de uma só vez, extraindo todas as informações e carregando-as nos componentes TMemo.

Classe Receiver

O primeiro passo é criar a classe Receiver, responsável por compreender a implementação de todas essas extrações. O código a seguir é um pouco extenso por englobar as três operações, porém, não deixa de servir como uma fonte de pesquisa de como extrair essas informações pelo Delphi. 🙂

Interface Command e classes Concrete Commands

No segundo passo, codificaremos a Interface Command, que representa uma abstração de todos os comandos que serão implementados. Vale destacar que essa Interface declara apenas um método – tradicionalmente chamado de Execute – no qual irá acessar os métodos do Receiver.

Em seguida, precisamos criar os comandos concretos, ou melhor, as classes Concrete Command, cada qual se responsabilizado por uma ação específica do Receiver:

Classe Invoker

O último passo é elaborar a classe Invoker, que possui uma codificação bem simples:

O código do botão “Executar Comandos” (do Client), conforme já dito anteriormente, será incumbido de criar as instâncias das classes acima e configurar as interações entre elas:

Ao clicar no botão, toda as extrações são realizadas em apenas uma ação, salvando-as em disco, e depois carregando-as nos componentes do formulário. Novamente, se quisermos atribuir outro Receiver ou adicionar/remover comandos, o mecanismo de execução, em sua integridade, continuará funcionando, visto que não há dependências entre as classes. Uma ideia seria adicionar um novo comando para enviar os arquivos gerados para o e-mail do suporte. 😉

Conclusão

Tenho algumas ressalvas. Você deve ter notado que este padrão de projeto estimula o “micro gerenciamento” de classes. Por um lado, é um ponto negativo por exigir a criação de várias classes, tornando a arquitetura um pouco volumosa. Por outro lado, cada classe tem uma responsabilidade pequena e única, promovendo o desacoplamento e facilitando a manutenção.

Além disso, você pode estar se perguntando: “Por que precisamos de um Invoker? Não bastaria apenas instanciar o Receiver e executar os três métodos em sequência?”. Sim, você pode. Se houver um formulário no sistema que exiba apenas a lista de programas instalados, só o Receiver realmente já é o suficiente.

Porém, a empregabilidade do Command vai além dessa necessidade. O Invoker pode manter as instâncias armazenadas e executá-las em horários específicos, como um agendador de tarefas. Além disso, por ter controle da lista de comandos, o Invoker pode aplicar configurações adicionais, validações, controles de execução é até disponibilizar um recurso de desfazer comandos, que é mencionado em algumas literaturas do Command.

Clique no link abaixo para baixar o projeto de exemplo deste artigo. Para personalizar a lista de comandos, adicionei três componentes TCheckBox, um para cada tipo de extração. Os comandos executados pelo Invoker serão apenas os que estão selecionados no formulário.
Experimente também adicionar novos Concrete Commands e/ou Receivers e observe a simplicidade de manutenção da arquitetura.

 

Espero que o artigo tenha sido útil, leitores!
Um grande abraço a todos!


 

André Celestino