15 de outubro de 2009

Design Patterns com Delphi : Decorator - parte 1

Normalmente, a modelagem de entidades que compartilhem comportamentos e/ou características em comum gera classes com relacionamentos do tipo "herança" quando transformada em programa. Esse tipo de modelagem tem seu comportamento definido em tempo de compilação, isto é, as instâncias de cada classe conhecem de antemão as operações que podem realizar. Imagine, porém, uma situação em que é preciso adicionar certa funcionalidade a uma única instância de uma classe. Criar uma herança não resolve esse problema pois todas as instâncias da classe herdada compartilhariam a funcionalidade.

O Design Pattern estrutural Decorator aborda esse tipo de necessidade. Ele foi estabelecido para permitir que se adicione novas funcionalidades (ou responsabilidades) a instâncias de uma classe, de modo que ela pode ser estendida em tempo de execução.

Embora o nome Decorator remeta à algo visual, o uso desse pattern não é restrito ao tratamento de interfaces gráficas. Esta é a aplicação mais comum dele (como no exemplo publicado no Wikipedia) mas pode também ser usado para estender regras de negócio. Por exemplo, numa aplicação para uma locadora onde é permitido alugar ou comprar certos itens (filmes, jogos, etc.), certas funcionalidades (exibir o item) e propriedades (título do item) se aplicam a qualquer tipo de item. No entanto, as ações de "alugar" ou "comprar" um item só são válidas em certos contextos da aplicação, como no momento de registrar o aluguel ou a devolução de um filme alugado.

Para implementar esse conceito, é preciso definir uma interface padrão com as ações comuns a todos os itens. Tanto os itens quanto as "decorações" são heranças dessa interface. A diferença é que as "decorações" são construídas para conter a referência a uma instância da interface e é essa instância quem executará as ações básicas para a decoração. Assim, não importa quantas "decorações" estejam adicionadas (empacotadas) umas nas outras pois o resultado final é sempre um objeto capaz de agir tanto como a interface base quanto como uma das decorações adicionadas. Pode parecer meio confuso à primeira vista mas uma olhada no diagrama UML ajuda a clarear.
Diagrama UML para Decorator

Se for necessário acrescentar outras responsabilidades a um item - uma ação para "empréstimo", por exemplo - não é necessário mexer na classe do Item, bastando criar a respectiva decoração e instanciá-la quando for necessário.

A nomenclatura para as classes envolvidas no Decorator é a seguinte :
O Component é quem declara a interface comum a todos os objetos que poderão ter responsabilidades adicionadas dinamicamente, isto é, trata-se de uma classe abstrata que indica o comportamento comum que se deve esperar de todos os integrantes da solução. No diagrama acima, esta interface é representada pela classe TWItem.
É chamado de ConcreteComponent cada um dos itens básicos que implementem a interface estipulada. São tidos como básicos porque não possuem nenhuma das decorações possíveis, embora possam ser atachados a uma ou mais delas. No exemplo, as classes TWFilme e TWJogo são considerados ConcreteComponents.
O Decorator é a classe base para criação de novas decorações (ou responsabilidades). Ela também é uma herança da interface comum (Component) mas com uma diferença : possui internamente um membro que é uma referência a um outro Component e é essa referência quem será responsável por executar as ações para o Decorator e suas heranças. No diagrama acima, o papel de Decorator é exercido pela classe TWItemDecorator.
Cada nova classe ConcreteDecorator permite adicionar uma ou mais ações à classe Component. Atachar uma dessas classes a um ConcreteComponent estende suas funcionalidades já que o ConcreteComponent passa a enxergar as ações disponibilizadas pelo Decorator em questão. É permitido aninhar diversos Decorators, o que na prática adiciona várias funcionalidades ao ConcreteComponent e amplia o escopo onde ele pode ser usado dentro das necessidades do programa modelado. As classes TWItemAlugel e TWItemVenda no diagrama anterior são exemplos de ConcreteDecorator; adicioná-las a um filme ou jogo proporcionará a possibilidade de alugá-los ou vendê-los, respectivamente. Tudo isso em tempo de execução.

Mostro em outro post como mapear essas relações usando Delphi e também como fazer as chamadas para obter o resultado desejado.

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.