21 de agosto de 2009

Design Patterns com Delphi : Prototype

O último dos Design Patterns criacionais estabelecidos pela GoF é o Prototype, cujo nome vem do fato de que objetos são instanciados com base num protótipo - ou modelo - existente. O Prototype, portanto, consiste na criação de objetos através da cópia do estado (valores das propriedades) de um objeto pré existente, gerando um clone desse objeto. Este tipo de cópia é mais útil quando há objetos aninhados, isto é, quando uma classe possui membros que são instâncias de outras classes, resultando numa hierarquia complexa de objetos cujo estado deve estar fielmente reproduzido na cópia feita.

Imagine, por exemplo, um sistema onde o usuário, ao se logar, é associado a um objeto contendo as configurações de sua sessão. Ele pode manter abertas várias telas ao mesmo tempo e aplicar temporariamente novas configurações em cada uma delas - o tamanho ou tipo do fonte (letras) ou o contraste das cores apresentadas para melhor visualização. Ao entrar na nova tela, é feita uma cópia da sessão, incluindo seus objetos internos, de modo que alterações introduzidas nas configurações da tela não afetam a configuração global existente na sessão original.

Para implementar o padrão Prototype trabalha-se basicamente com 3 classes, representadas no diagrama UML abaixo.

Diagrama UML para Prototype
A classe TWPrototype é abstrata e apenas introduz o comportamento esperado para um protótipo, que é poder gerar um clone de si mesmo. As classes TWSessao, TWFonte e TWColors são heranças de TWPrototype, o que siginifica que elas podem criar cópias idênticas de si mesmas. Veja que a classe TWSessao possui propriedades internas dos tipos TWFonte e TWColors, o quê permite à sessão repassar a mensagem de clonagem para garantir que todas as propriedades têm realmente os mesmos valores da sessão original, não importando que essas propriedades também são objetos. E TWTela, referenciada na documentação do padrão como classe Client, é quem chama o método de clonagem, utilizando uma cópia idêntica da sessão para aplicar alterações temporárias - quem até podem se tornar permanentes, se for o desejo do usuário.

O Delphi suporta o conceito de interface (como o Java), o que facilita a implementação do Prototype. As classes TWSessao, TWColors e TWFonte não mantem entre si uma relação conceitual que justique colocá-las como herança de uma mesma classe base. Por isso, crio o TWPrototype como uma interface e digo que as outras classe implementam esta interface. Isso deixa o código mais fácil de manter já que a hierarquia das classes fica mais clara. Em Delphi, a declaração das classes seria algo assim:
TWPrototype=interface
function Clone : TInterfacedObject;
end;

TWColors = class(TInterfacedObject,TWPrototype)
{ outros membros aqui}
public
function Clone : TInterfacedObject;
end;

TWFonte = class(TInterfacedObject,TWPrototype)
{ outros membros aqui}
public
_Familia: String;
_Tamanho: String;
_Bold : Boolean;
function Clone : TInterfacedObject;
end;

TWSessao = class(TInterfacedObject,TWPrototype)
{ outros membros aqui}
public
_Id : String;
_Fonte: TWFonte;
_Colors: TWColors;
function Clone : TInterfacedObject;
end;

TWTela = class
{ outros membros aqui}
public
_Sessao : TWSessao;
constructor Create (ASessao: TWSessao);
end;

Para obter sua cópia da configuração da sessão, o construtor da tela apenas chama o método Clone da interface:
constructor TWTela.Create (ASessao: TWSessao);
begin
_Sessao := ASessao.Clone As TWSessao;
end;

Na TWSessao, o método Clone introduzido pela interface copia os dados atômicos "na mão" e clona os dados que são complexos:
function TWSessao.Clone : TInterfacedObject;
var nova : TWSessao;
begin
nova := TWSessao.Create;
nova._Id := _Id;
nova._Fonte := _Fonte.Clone As TWFonte;
nova._Colors := _Colors.Clone As TWColors;

Result := nova;
end;

Para o TWColors e TWFonte, o método Clone apenas copia as propriedades internas, que são todas atômicas (não há instâncias de classes entre eles).

O exemplo é simples para ser didático mas esse Pattern pode ter uma hierarquia bem mais complexa. Pode, por exemplo, estar em associação com o Pattern Abstract Factory para criar objetos "clonáveis" sem que a classe Client saiba exatamente o tipo real do objeto a clonar. Um exemplo disso seria ter um objeto com o perfil atual do usuário e esse perfil pudesse ser um dentre diversas especializações (Administrador, usuário do Financeiro, do marketing, etc.) e parte do perfil pudesse ser temporariamente modificado.

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.