Para facilitar a compreensão, reproduzo novamente abaixo o diagrama UML que exemplifica o uso do FlyWeight:
Baseado no diagrama, teremos que declarar a classe TWFonte que atua como uma interface, isto é, sua característica principal é introduzir o comportamento (as funções) que permite compartilhar dados intrínsecos da classe e aplicar temporariamente a esses dados outras informações externas (os dados extrínsecos) particulares a cada contexto.
É bastante comum que esse tipo de classe seja abstrata pura, isto é, que não possua variáveis internamente - esta característica é reservada às versões concretas da classe - mas isso não é uma obrigação. No exemplo do diagrama, há informações comuns aos tipos de Fonte que fazem sentido ser colocados nessa classe:
TWFonte = class
private
{ Nome do Fonte }
_Nome : String;
{ Lista de imagens disponíveis para o Fonte }
_Imagens : TList;
constructor Create(ANome : String); overload;
public
constructor Create;reintroduce; overload;
procedure Desenha(AContext: TWContext; AStyle: TWStyle) = 0;
end;
{ ... }
constructor TWFonte.Create(ANome : String);
begin
_Nome := ANome;
{ ... }
end;
constructor TWFonte.Create;
begin
Raise Exception.Create('Classe deve ser construída a partir da Factory');
end;
private
{ Nome do Fonte }
_Nome : String;
{ Lista de imagens disponíveis para o Fonte }
_Imagens : TList;
constructor Create(ANome : String); overload;
public
constructor Create;reintroduce; overload;
procedure Desenha(AContext: TWContext; AStyle: TWStyle) = 0;
end;
{ ... }
constructor TWFonte.Create(ANome : String);
begin
_Nome := ANome;
{ ... }
end;
constructor TWFonte.Create;
begin
Raise Exception.Create('Classe deve ser construída a partir da Factory');
end;
Veja que há dois construtores com o nome de Create, um privado e outro público. A intenção por trás disso é forçar o uso da classe de Factory quando for necessário obter instâncias do TWFonte já que é o Factory quem garante que apenas uma instância de cada herança concreta existe e, em última análise, garante o sucesso do compartilhamento dos dados intrínsecos de cada fonte. Para maiores detalhes sobre essa técnica de esconder um construtor, veja nesse endereço a seção Hiding a Constructor.
Como dito no parágrafo anterior, a classe Factory para o pattern Flyweight funciona de forma ligeiramente diferente daquela estabelecida pelo pattern Factory Method. Aqui será preciso ter uma instância da classe Factory (provavelmente um Singleton) para gerenciar as instâncias criadas:
TWFonteFactory = class
private
_Fontes : TStringList;
{ ... }
public
function GetFonte (ANome: String) : TWFonte;
end;
{ ... }
function TWFonteFactory.GetFonte (ANome: String) : TWFonte;
var idx : integer;
begin
Result := nil;
idx := _Fontes.IndexOf(ANome);
if (idx >= 0) then
Result := _Fontes.Objects[idx] As TWFonte
else begin
if ANome = 'Arial' then
Result := TWArial.Create
else
if ANome = 'TimesNewRoman' then
Result := TWTimesNewRoman.Create;
end;
end;
private
_Fontes : TStringList;
{ ... }
public
function GetFonte (ANome: String) : TWFonte;
end;
{ ... }
function TWFonteFactory.GetFonte (ANome: String) : TWFonte;
var idx : integer;
begin
Result := nil;
idx := _Fontes.IndexOf(ANome);
if (idx >= 0) then
Result := _Fontes.Objects[idx] As TWFonte
else begin
if ANome = 'Arial' then
Result := TWArial.Create
else
if ANome = 'TimesNewRoman' then
Result := TWTimesNewRoman.Create;
end;
end;
Talvez a diferença mais importante nesse Factory é que ele mantem internamente as instâncias criadas e, quando solicitado, retornam um ponteiro para uma instância para uso externo. Então, embora não apareça no trecho de código acima, o destructor da classe TWFonteFactory deve destruir todas as instâncias de TWFonte criadas e armazenadas por ela. Com isso, deve-se tomar cuidado para não usar uma instância do TWFonte após o destrutor da Factory ter sido chamado.
As últimas classes relevantes para o FlyWeight são as que implementam concretamente a interface de compartilhamento. No exemplo, as ConcreteFlyWeight são a TWArial e a TWTimesNewRoman, que são codificadas como heranças simples:
TWArial = class(TWFonte)
public
procedure Desenha(AContext: TWContext; AStyle: TWStyle);
end;
{ ... }
procedure TWArial.Desenha(AContext: TWContext; AStyle: TWStyle);
var lImagem : TImagem;
begin
lImagem := GeraImagem (_Imagens, AStyle, AContext.Texto);
DesenhaImagem (AContext, lImagem);
end;
public
procedure Desenha(AContext: TWContext; AStyle: TWStyle);
end;
{ ... }
procedure TWArial.Desenha(AContext: TWContext; AStyle: TWStyle);
var lImagem : TImagem;
begin
lImagem := GeraImagem (_Imagens, AStyle, AContext.Texto);
DesenhaImagem (AContext, lImagem);
end;
A destacar nesse trecho é a forma como a função Desenha faz uso das propriedades intrínsecas e dos parâmetros para obter o resultado desejado. Os parâmetros são propriedades extrínsecas - isto é, de fora da classe - e, portanto, não são compartilhados. A função interna GeraImagem cria uma imagem para cada caractere do texto e concatena todas elas para gerar a imagem correspondente ao texto informado no Contexto. A imagem de cada caractere é escolhida na lista interna de acordo com o estilo e tamanho requisitado pelos parâmetros. Para finalizar, a função interna DesenhaImagem toma a imagem gerada e a desenha usando as informações estipulada pelo contexto - handle do canvas, localização, etc..
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.