Artigo no estilo Mentoring
Enquanto os padrões criacionais resolvem problemas relacionados à criação de objetos, e os estruturais aos modelos de estruturas de classes, os padrões comportamentais resolvem problemas relacionados principalmente à interação e comunicação entre objetos, que normalmente causam forte acoplamento e dependência.
Para cada padrão de projeto, serão apresentados as suas intenções, benefícios, vantagens e participantes conforme definidos no livro do GoF. A seguir, será apresentado um exemplo prático em Delphi mostrando como criar um framework com as classes de cada padrão e uma aplicação que consome este framework.
O diagrama UML de classes é apresentado para mostrar o relacionamento entre os participantes.
Observer
A intenção do padrão Observer é definir uma dependência um-para-muitos entre objetos, de modo que quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.
O padrão pode ser aplicado quando: uma abstração tem dois aspectos, um dependente do outro. Encapsulando esses aspectos em objetos separados, permite-se variá-los e reutilizá-los independentemente; quando uma mudança em um objeto exige mudanças em outros, e você não sabe quantos necessitam ser mudados; quando um objeto deveria ser capaz de notificar outros objetos sem fazer hipóteses, ou usar informações, sobre quem são esses objetos.
Em outras palavras, você não quer que esses objetos sejam fortemente acoplados. Entre os principais benefícios podemos citar: acoplamento abstrato entre Subject e Observer e suporte para comunicação do tipo “broadcast”.
Participantes:
· Subject – conhece os seus observadores. Um número qualquer de objetos Observer pode observar um Subject;
· Observer – define uma interface de atualização para objetos que deveriam ser notificados sobre mudanças em um Subject;
· Concrete Subject – armazena estados de interesse para objetos ConcreteObserver; envia uma notificação para os seus observadores quando seu estado muda;
· ConcreteObserver – mantém uma referência para um objeto ConcreteSubject; armazena estados que deveriam permanecer consistentes com os do Subject; implementa a interface de atualização de Observer, para manter seu estado consistente como o do Subject;
No Delphi, a classe TComponent implementa o padrão Composite, permitindo que um objeto seja formado a partir da composição de vários outros componentes, guardando internamente uma coleção que referencia cada uma destes, chamada FComponents, do tipo TList<TComponent> (coleção genérica de componentes). Isto permite, por exemplo, que o Delphi implemente o mecanismo de componente “proprietário”, mais conhecido como Owner, que gerencia o ciclo de vida de um objeto conforme a composição.
TComponent define ainda uma lista de observadores, armazenados no atributo FObservers, do tipo TObservers (você pode verificar esta estrutura na unit System.Classes). Podemos notar uma implementação mais antiga do padrão Observer em TComponent, através do mecanismo de Notification (notificação).
O método Notification de TComponent recebe como parâmetro um componente e uma operação, do tipo TOperation (opInsert, opRemove). FreeNotication é um método que faz uso do padrão Observer para monitorar quando um componente é excluído, por exemplo, do form designer.
É graças a esse mecanismo de observação que, por exemplo, quando excluímos uma instância de algum TDataSet como um ClientDataSet que esteja sendo referenciado na propriedade DataSet de um TDataSource, o mesmo receberá uma notificação informando que houve uma exclusão, removendo assim a referência ao objeto e evitando um Access Violation.
A Listagem 1, Listagem 2 e Figura 1 mostram um exemplo de implementação do padrão Observer em Delphi. Aqui foram definidas duas classes, chamadas de Venda e Balanco, representando operações em um sistema de vendas. Neste caso, quando um balanço está em andamento, não é possível iniciar uma venda.
Dessa forma, uma classe precisa monitorar a outra, verificando constantemente o seu estado. Normalmente programadores fazem uso do componente TTimer para tarefas deste tipo, definindo um tempo curto para disparo do evento de verificação (ex. 1 segundo), e constantemente consultando propriedades ou estado de componentes públicos de um formulário alvo para atualizar o formulário de origem.
Esta, além de não ser uma boa prática devido ao forte acoplamento causado, cria um overhead desnecessário de notificações, visto que a verificação de estado p ...