É bastante comum no desenvolvimento de software a situação em que um mesmo tipo de comando pode ser ativado por diferentes meios. Por exemplo, no
ERP Omega da
ABC71 um mesmo processo ou relatório pode ser executado diretamente pelo usuário ou essa execução pode ser agendada para executar mais tarde, num horário mais apropriado.
Esse tipo de situação é abordada pelo
Design Pattern comportamental Comando. Nesse Pattern, uma requisição é encapsulada como uma objeto, facilitando a configuração de chamadas a essa requisição em diversas circunstâncias. Tendo comandos como um objeto, é possível criar filas de execução de comandos (como num
Wizard), implementar o recurso de "desfazer" em um programa (percorrendo a lista de comandos executados e chamando um método "undo" que deve existir em cada objeto) ou até mesmo permitir a gravação de macros em seu programa (na prática, uma lista de comandos criada e mantida diretamente pelo usuário).
No
ERP da
ABC71, temos um conceito de "tabelas integradoras" onde softwares de terceiros podem armazenar informações que devem ser importadas por nós. O processo de importação lê os registros gravados nessas tabelas e aplica a eles nossas regras de negócio de modo que os dados são corretamente incorporados ao
ERP. Cada tipo de importação pode ser encarado como um comando separado e o usuário pode escolher numa tela quais os comandos e respectivos parâmetros. Os comandos poderiam também ser acessados individualmente, fora da tela. O diagrama abaixo mostra uma visão simplificada da solução para esse cenário usando o padrão
Command:
De acordo com o objetivo pretendido, pode haver pequenas variações nas relações entre as classes mas o que está representado no diagrama é uma boa base para o
Command. No quadro abaixo aparece a nomenclatura usual para as classes participantes da solução:
Comand é uma interface que estabelece as operações que estarão disponíveis num comando. No exemplo acima, a interface
TWComandoBase detem esse papel ao introduzir as operações
Execute e
Undo. Prevendo que nem todo comando poderá ser desfeito, há também a função
IsUndoable, permitindo que cada comando estabeleça se poderá ou não ser desfeito.

O
Receiver é qualquer classe que tenha o conhecimento de como executar a ação associada a um comando. Trata-se, portanto, do objeto que executa de fato a ação requisitada. No diagrama, esse é o papel da classe
TWImportableObj e suas heranças.

Um
ConcreteCommand provê a ligação entre o objeto
Receiver e a ação esperada para o comando. Isto implica que, ao criarmos a instância do
ConcreteCommand, devemos vincular um
Receiver a ela, promovendo o desacoplamento entre este
Receiver e quem irá invocar o comando (o
Invoker). Quem invoca o comando apenas espera que ele seja executado, sem se preocupar com os detalhes da implementação; quando se requisita ao comando que execute a ação, o
ConcreteCommand redireciona a requisição de forma transparente para o
Receiver. No diagrama acima, aparecem duas classes que agem como
ConcreteComand :
TWComandoCarga e
TWComandoExecProc.

A classe
Invoker é quem solicita a um comando que execute a ação associada. No nosso exemplo, a classe
TWComandos executa esse papel de uma forma uma pouco mais elaborada. Aqui, ele aceita uma lista de comandos - quaisquer objetos que implementem o
TWComandoBase - e os executa sequencialmente sem precisar ter conhecimento sobre como cada comando realiza sua operação. Ou seja, o
Invoker está desacoplado do
Receiver.

A responsabilidade do
Client é criar corretamente todos os
ConcreteComand necessários e ajustar o
Receiver apropriado para cada um. Os objetos assim criados são adicionados à lista de comandos representada pelo
Invoker e este é acionado para executar cada comando. No exemplo, o papel de
Client é reservado para a classe
TWFormImportaRegs, que neste caso é uma tela que oferece ao usuário a oportunidade de selecionar os comandos que ele deseja executar bem como suas respectivas configurações.
Note que é possível criar instâncias isoladas das heranças de
TWImportableObj e requisitar a importação separadamente a partir delas. A vantagem de fazer isso usando o
Invoker é que este flexibiliza o uso dos comandos, possibilitando, por exemplo, a execução de importações (e quaisquer outros comandos que implementem a interface
TWComandoBase) em agendamentos, em qualquer ordem.
Volto no próximo post para mostrar um mapeamento das classes envolvidas usando o Delphi.
Nenhum comentário :
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.