25 de abril de 2011

Design Patterns com Delphi: Observer - Parte I

Quando projetamos um sistema computacional baseado em classes, temos a tendência de focar aspectos isolados, tratando grupos de classes associadas de forma que resolvem um problema particular no domínio da aplicação. Na verdade, isso é uma característica intrínseca de projetos orientados a objetos, pois a responsabilidade de cada classe deve estar bem definida para que se consiga extrair o melhor desse tipo de abordagem: induzir o reaproveitamento de código e facilitar a manutenção do sistema.

Mesmo assim, muitas classes que aparentemente não guardam relação entre si podem precisar conhecer mudanças de estado umas das outras. Tal situação é alvo do Design Pattern comportamental Observer. Nele, uma classe expõe uma espécie de serviço por assinatura relativo a uma determinada característica sua. Outros objetos, então, podem assinar o serviço para serem notificados sempre que essa característica sofrer uma manutenção relevante – quando ela for alterada ou acessada, por exemplo.

Um cenário onde esse padrão pode ser aplicado ocorre quando há uma classe representando uma massa de dados que é acessível por diversas outras classes. É o caso das planilhas de cálculo, onde os mesmos dados podem ser exibidos numa tabela ou num gráfico ou ainda servirem de base para cálculos apresentados em outras tabelas. É interessante, portanto, que essas classes sejam notificadas toda vez que houver uma alteração na massa de dados, pois assim elas poderão refletir imediatamente quaisquer alterações e sempre apresentar informações atualizadas para o usuário. Não é à toa que a descrição acima remeta tão fortemente ao conceito de MVC (Model-View-Controller) : a parte View é tradicionalmente implementada como um Observer.

Outro exemplo de aplicação desse padrão é em sistemas com auditoria. Aqui, classes que permitam manutenção em dados podem ser preparadas para prover um serviço que reporte certas alterações. Então, essas alterações podem ser registradas no banco de dados para uma eventual auditoria ou disparar a emissão de alertas a usuários com credenciais especiais que tenham solicitado receber notificações sobre as alterações. O diagrama UML abaixo mostra uma solução para esse cenário usando o Observer.
Diagrama UML para o padrão Observer

Note que o padrão é estruturado de forma que a classe com regras de negócio expõe o serviço de notificação mas somente quando surge necessidade (ou por opção do usuário) é que as demais classes fazem a assinatura para receber tais notificações. Isto é, a notificação não é compulsória; ela é feita somente sob demanda da própria classe interessada.

Os participantes de uma solução com esse design pattern recebem os nomes explicados no quadro a seguir:
O Subject é uma classe que introduz os meios pelos quais o serviço por assinatura é publicado. É ela quem dita como outras classes podem se cadastrar para receber as notificações e, se for possível suspender o cadastro, como isso poderá ser feito. Por isso, ela deve ser capaz de manter referências às instâncias das classes que solicitam notificações, sendo efetivamente a provedora do serviço de assinatura. No diagrama anterior, esse papel cabe à classe TWBusinessObj. Obviamente, nada impede que essa classe execute outras tarefas, como se deduz do diagrama.

O Concrete Subject é a classe que possui alguma característica de interesse que poderá ser monitorada. Ela é, portanto, a origem real das modificações que devem ser notificadas. Ela deve ainda fornecer mecanismos para que as classes que solicitarem notificações possam ter acesso ao seu estado interno, recuperando as informações alteradas. Além disso, deve estar apta a disparar as notificações sempre que isso for necessário. No exemplo, este é o papel das classes TWPedidoVenda e TWTituloAPagar. Por ser uma herança do Subject, este tipo de participante da solução compartilha o mecanismo de gerenciamento de assinaturas.

Observer é o nome dado à classe que estipula a forma como um assinante do serviço do Subject receberá as notificações. É comum que esse papel seja representado por uma classe abstrata que apenas introduza a função destinada a utilizar os novos dados atualizados. É o caso de TWBOObserver no diagrama anterior. Veja que a classe do tipo Subject (no exemplo, TWBusinessObj) permite anexar e remover instâncias de TWBOObserver (o Observer). Por isso, qualquer herança desse Observer pode se cadastrar para receber as notificações.

Um Concrete Observer implementa a função especificada pelo Observer, usando de fato o estado interno do Subject que originou a notificação para realizar quaiquer operações que ele julgue necessárias. Este é o papel das classes TWAuditoria e TWUsuario no exemplo.

O padrão Observer é dos mais utilizados, tanto que alguns frameworks, como Java e .Net, já trazem embutidos mecanismos para agilizar sua implementação. Isso se dá através da introdução de interfaces (com nomes como IObserver e IObservable) ou de palavras chaves da própria linguagem de programação (delegate e event). A VCL também usa o Observer em vários pontos, principalmente no tratamento de eventos de seus componentes.

No próximo post, apresento uma implementação desse exemplo usando Delphi.

Um comentário :

Anônimo disse...

Eu desenvolvo em Delphi e achei este site por acaso. Luis Gustavo muito obrigado por seu serviço caprichado. Seus artigos são muito bem explicados e cheios de exemplos práticos. Nota dez!

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.