Identity Map>: uma instância, múltiplas referências

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

Identity Map: uma instância, múltiplas referências

Tempos atrás, resolvi adotar o padrão Identity Map para meus objetos de negócios, que se usa para ter uma única instância de cada objeto num cache da classe e referenciar sempre aquela mesma instância quando for preciso acessar o objeto. Desta forma, é possível evitar de se ter cópias com propriedades diferentes do mesmo objeto em diferentes partes do programa e também de ser preciso carregar desnecessariamente o objeto do banco de dados sempre que precisar acessá-lo. O problema é que as implementações deste padrão nas formas como encontrei na Internet têm alguns inconvenientes.

A forma mais simples seria ter um procedimento que busca o objeto numa coleção que serve como cache ou o carrega do banco de dados no caso de não o encontrar no cache. Quando carregado do banco de dados, o objeto é adicionado ao cache. Uma referência ao objeto é retornada para o ponto do programa que precisa acessar o objeto. Desta forma, qualquer modificação feita no objeto em qualquer parte do programa será refletida onde quer que ele esteja sendo referenciado, já que se trata da mesma instância em todas as referências.

Uma inconveniência da solução acima é que sempre que uma versão mais atual do objeto tenha que ser carregada do banco de dados, será preciso substituir a instância do cache por uma nova e também alterar todas as referências que se possa ter ao objeto dentro do programa naquele momento. Não é preciso pensar muito para ver que isto é bastante inconveniente.

Uma solução para este problema é trabalhar, não com o objeto diretamente, mas com um proxy dele. O proxy é um objeto que tem a mesma interface de outro, mas que não implementa concretamente nenhuma funcionalidade, apenas mantém dentro de si uma referência a um objeto plenamente funcional e repassa as chamadas feitas aos seus métodos e às suas propriedades para os correspondentes no objeto funcional. Se adotarmos trabalhar com proxies para os objetos, podemos ter múltiplos proxies referenciando a mesma instância do cache e fazer com que o cache dispare um evento toda vez que a instância de um objeto for substituída para que os seus proxies sejam notificados e substituam a referência interna pela nova instância. Desta forma, restringimos o código que precisa tratar as atualizações apenas aos proxies.

Porém, além da complicação de ter de criar uma classe de proxy para cada classe de objeto de negócio, ainda não nos livramos das inconveniências, pois há momentos em que ter múltiplas referências ao mesmo objeto também traz problemas. Num contexto, a referência pode ser usada para por o objeto em edição e passar a disparar eventos tratados por outros objetos que fazem parte daquele contexto, mas que estarão sendo tratados inconvenientemente também por objetos de outros contextos em que ele estiver sendo referenciado. Sendo assim, me parece que a solução pode ser, não a de manter um cache do objeto em si, mas apenas dos seus dados. Cada instância de um objeto teria uma variável interna para armazenar os valores dos seus dados e seria notificada de qualquer atualização dos dados no cache. Ao receber esta notificação, a instância do objeto saberia da necessidade de reconfigurar suas propriedades e faria isto no momento mais conveniente. Desta forma, podemos ter várias instâncias do mesmo objeto com suas propriedades convenientemente sincronizadas. Podemos, por exemplo, ter o mesmo objeto exibido em dois formulários e estarmos a editá-lo num formulário enquanto observamos suas propriedades atuais em outro. Somente quando fossem salvas as novas configurações da instância em edição é que as instâncias do objeto se igualariam nos dois formulários.

Um problema que surge sempre que se queira fazer um cache de dados de objeto para esta finalidade é que a coleção do cache irá crescendo se os itens do cache não forem removidos quando não mais estiverem sendo usados em parte alguma do programa. Para isto, é preciso que toda criação de uma nova instância de um objeto seja comunicada ao cache para que um contador de instâncias daquele objeto seja incrementado. Por outro lado, toda destruição de uma instância deve também ser comunicada ao cache para que o contador seja decrementado. Quando o contador de instâncias chegar a zero, o cache remove os dados relativos àquele objeto. Assim sendo, os objetos que seguirem este padrão precisarão comunicar o cache das suas finalizações.