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.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;
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;
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;
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.