Visual Basic, VB .NET, ASP, Active X, Access, SQL Server

Dica de Design no VB: como por todos os seus objetos em comunicação.

Há situações em que você tem relações complexas entre seus objetos e precisa de que um acontecimento ocorrido em um objeto seja comunicado a outros para que se ajustem ao fato. Isto é feito disparando eventos que serão tratados pelos objetos interessados em se ajustarem ao que ocorreu. Estes  objetos poderão disparar novos eventos no curso do tratamento do evento inicial e criar uma conversação ampla e complexa dentro do conjunto de objetos. No entanto, entre os objetos que precisam disparar os eventos e os que irão tratar os eventos precisa haver algum tipo de relacionamento. O que normalmente se faz é armazenar uma referência ao objeto disparador dos eventos no módulo de classe do objeto que irá tratá-los. Declarando uma variável do tipo da classe do objeto disparador com a cláusula WithEvents faz com que o objeto apareça na combo de objetos, e seus  eventos na  combo de eventos da janela de código. Mas isto só vale quando você tem um número limitado, pequeno, bem determinado de objetos que irão disparar os eventos e sabe quais são. Na programação orientada a objetos, os objetos podem ser criados e destruídos em qualquer parte da aplicação. Colocá-los diretamente em relação quando nascem e remover este relacionamento quando são destruídos é uma tarefa impraticável.

A solução é ter um objeto global para intermediar estes relacionamentos. Poder-se-ia pensar em uma coleção central que armazenasse uma referência para cada novo objeto criado e que removesse esta referência na destruição do objeto. Quando um objeto tivesse que disparar um evento, chamaria um método genérico do objeto coleção, que percorreria a coleção e chamaria um método em cada objeto avisando do evento. Mas você teria que incluir e excluir os objetos nesta coleção com código que fosse executado em cada criação e destruição de objeto. Até faz sentido codificar a inclusão do objeto no seu evento Initialize, mas não será possível fazer a exclusão no evento Terminate, pois este não será disparado enquanto houver uma referência a ele em algum lugar - como na coleção, por exemplo. Você teria que saber exatamente quando o objeto perde todas as referências além daquela mantida na coleção para então removê-lo. Ou seja, não é uma boa solução para este problema.

Para um exemplo do que será dito abaixo, clique aqui .

A idéia de ter um objeto central pode ser melhorada. Se, ao invés de um objeto central, que referenciasse todos os demais, tivéssemos uma referência a este objeto em cada objeto do conjunto? O objeto central dispararia eventos a pedido de quem quisesse dispará-los para o conjunto. Aqueles que estivessem interessados no evento disparado, tratariam o evento. Caso contrário, poderiam ignorá-lo. Haveria um publicador global de mensagens referenciado em cada objeto por uma variável declarada com a cláusula WithEvents. O publicador global poderia ter um EventoGenerico disparado sempre que lhe chamassem o método PublicarEvento. PublicarEvento receberia os argumentos do evento a ser disparado e chamaria RaisEvent EventoGenerico.(argumentos) .  PublicarEvento poderia receber como argumentos: o identificador do evento, um Variant com uma matriz de dados para o evento e uma referência ao objeto remetente do evento. Todos estes dados seriam repassados como argumentos para o EventoGenerico. Como o disparo de um evento pode sempre gerar novas chamadas ao método PublicarEvento, haveria necessidade de tratar a reentrância neste método. Uma variável estática para identificar a chamada por contagem resolveria o problema. Ao final da chamada identificada como 1, ela seria posta novamente em zero à espera de uma nova chamada inicial. Caso ocorressem erros durante o tratamento de um dos evento da cadeia, haveria a necessidade de interromper o tratamento dos eventos já disparados e restaurar o conjunto de objetos ao seu estado inicial. Para isto, seria preciso que o conjunto dos objetos tivesse algum nível de comando central capaz de serializar todo o conjunto e retornar um Variant contendo os dados da totalidade. Estes dados seriam armazenados na variável estática varSerialização do método PublicarEvento. Quando um erro ocorresse, os dados seriam passados ao comando central dos objetos para que se reconstituísse a partir deles. Para saber imediatamente quando algum objeto encontrou erros no tratamento de um dos eventos da cadeia, os eventos receberiam por referência o argumento booleano "Cancelar".  Ao atribuir True a "Cancelar", um objeto que estivesse tratando um dos eventos da cadeia impediria que todos os demais seguissem tratando eventos já disparados. Isto porque os objetos poderiam testar este argumento antes de seguirem no tratamento de cada evento. Dentro do método PublicarEvento, "Cancelar" seria mantido como uma variável estática e inicializada sempre que ocorresse uma chamada inicial ao método. Após disparar um evento, o método PublicarEvento a testaria e, se estivesse em True, PublicarEvento se encerraria gerando um erro para avisar da interrupção da cadeia de eventos a quem o tivesse chamado .

Um problema que surge desta conversação complexa entre objetos, é que dois objetos podem ter relações de mútua dependência e dispararem eventos um para o outro. Isto geraria um loop de eventos. Para evitar isto, o objeto publicador global poderia manter uma variável onde os objetos registrariam dados que lhes permitissem checar se já não trataram um determinado evento. O publicador global teria um método "JaTratei" que retornaria True se o objeto que o chamou lhe passasse uma string encontrada nos dados de registro de tratamento, e False em caso contrário. Neste último caso, a string seria armazenada para ficar o registro de que o evento já estaria sendo tratado. Os objetos poderiam administrar a forma de evitar este loop de eventos compondo strings de registro como bem quisessem, porém seguindo um padrão que garantisse unicidade. Uma idéia seria iniciar a string com os ponteiro para o objeto receptor e para o objeto remetente obtidos com ObjPtr. Isto seria combinado com a identificação do evento e mais alguma informação relevante para evitar ou controlar os limites do loop de interações. Cada informação seria separada por um caractere próprio para separação.

Uma idéia que surge disto tudo é que seria possível usar este esquema para interrogar o conjunto dos objetos sobre quais consequências cada objeto sofreria se fosse disparado um certo evento. O truque seria passar o identificador dos eventos com valor negativo. Ao entrar num tratamento do evento genérico, o objeto verificaria se o identificador do evento é negativo. Neste caso, o objeto nada faria além de chamar o método AdicionarAviso do objeto publicador global. Isto para adicionar um aviso do que lhe ocorreria se tratasse aquele evento. Caso tivesse que disparar outros eventos a partir daquele tratamento, o faria passando-os também com identificador negativo. Ao final, uma chamada ao método PegarAvisos do objeto publicador glogal poderia retornar o texto completo dos avisos e exibí-lo ao usuário. O usuário poderia então confirmar ou cancelar uma ação que iria iniciar aquela cadeia de eventos.

O modelo acima pode ser otimizado para cada uso substituindo o evento genérico por eventos específicos para cada identificador de evento. Assim fazendo, os objetos nunca seriam chamados a verificar um evento genérico apenas para descobrir que não lhes interessa tratar este ou aquele evento, mas apenas seriam chamados a tratar os eventos para os quais possuissem procedimentos de tratamento.