16 de outubro de 2009

Design Patterns com Delphi : Decorator - parte 2

Neste post vou dar continuidade ao conceito do Design Pattern Decorator, mostrando como mapeá-lo em classes no Delphi. Vou usar como base o diagrama UML que publiquei no post anterior, isto é, mostrarei a estrutura de código para o exemplo de locadora e como estender dinamicamente a funcionalidade de um item.
Diagrama UML para Decorator

Conforme retratado no diagrama acima, a primeira providência é criar a classe abstrata que servirá de base tanto para itens da locadora como para os Decorators definidos.
type
TWItem = class
{ ... }
_Titulo : String;
_Descricao : String;

procedure Exibir;virtual;abstract;
constructor Create (ATitulo: String;ADescr: String);virtual;
end;

{ ... }

constructor TWItem.Create (ATitulo: String;ADescr: String);
begin;
_Titulo := ATitulo;
_Descricao := ADescr;
{ ... }
end;

As classes TWFilme e TWJogo são especializações simples, bastando codificar heranças do TWItem para tratá-las. Elas representam itens da locadora sem qualquer responsabilidade extra, atuando mais ou menos como um cadastro básico no programa.
type
TWFilme = class(TWItem)
{ ... }
procedure Exibir;override;
end;

TWJogo = class(TWItem)
{ ... }
procedure Exibir;override;
end;

{ ... }

procedure TWFilme.Exibir;
begin
{ ... }
MostraTitulo;
MostraDescr;
MostraOutros;
{ ... }
end;

Já a classe base para representar os Decorators permitidos para itens da locadora tem que armazenar uma referência para o item ao qual estão vinculados. Esta é a diferença fundamental entre um item puro e um item de decoração.
type
TWItemDecorator = class(TWItem)
{ ... }
_Item : TWItem;
procedure Exibir;override;
constructor Create (AItem: TWItem);reintroduce;virtual;
end;

{ ... }

constructor TWItemDecorator.Create (AItem: TWItem);
begin
inherited Create (AItem._Titulo, AItem._Descricao);
_Item := AItem;
{ ... }
end;

procedure TWItemDecorator.Exibir;
begin
_Item.Exibir;
{ ... }
end;
Observe o uso da palavra reservada reintroduce. Ela permite que eu crie uma nova função com o nome de outra já existente na classe-pai dessa herança e, ao mesmo tempo, impede o uso da função original por outras classes do programa. Como neste caso se trata do construtor, na prática estou obrigando o programador a instanciar a classe usando a versão que aceita como parâmetro a referência para um item. Internamente no construtor eu tenho acesso a versão original da função (construtor). Então, uso o item informado no parâmetro da nova função para suprir os valores exigidos pela função original.

O objetivo do Decorator base é garantir que cada novo Decorator criado como herança dele se comporte como um TWItem. Veja pelo corpo da função Exibir acima que este objetivo é atingido usando a referência controlada pelo Decorator. Ao invés de implementar de novo a função, o Decorator simplesmente chama a função equivalente contida na referência do TWItem.

Os Decorators definidos pelo diagrama UML do exemplo, então, são declarados como heranças simples do TWItemDecorator. Cada um deles introduzirá apenas o comportamento adicional a que se propõem executar pois, como foi dito, o Decorator base se responsabilizará por emular o comportamento esperado de um TWItem usando para isso a referência passada para ele.
type
TWItemAluguel = class(TWItemDecorator)
{ ... }
_ValorAluguel : Double;
_ValorMulta : Double;
procedure Alugar;
procedure Devolver;
end;

TWItemVenda = class(TWItemDecorator)
{ ... }
_ValorVenda : Double;
procedure Vender;
end;
E como é que isso deve ser usado no programa ? Nos pontos onde apenas o item em si é necessário, instancie um item com o tipo desejado e faça uso dos seus métodos.
var lItem : TWItem;
begin
lItem := TWFilme.Create ('Blade Runner', 'descrição ...');
lItem.Exibir;
{ ... }
Ao atingir um ponto onde seja necessário se comportar como um item "vendável" ou "alugável", basta instanciar o(s) Decorator(s) informando a referência a um TWItem qualquer, que pode ser um item puro ou um outro Decorator já instanciado anteriormente.
var lItem : TWItem;
lAluga : TWItemAluguel
lVenda : TWItemVenda;
begin
lItem := TWFilme.Create ('Blade Runner', 'descrição ...');
lAluga := TWItemAluguel.Create (lItem);
lAluga.Alugar;
lAluga.Exibir;

{ Passa o decorator de aluguel no parâmetro da referência, sem prejuizo para a funcionalidade do TWItem. }
lVenda := TWItemVenda.Create (lAluga);
lVenda.Exibir;
lVenda.Vender;
{ ... }
Os itens "alugáveis" e os "vendáveis" continuam podem ser usados em lugares onde for esperado o uso de um TWItem.

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.