27 de julho de 2009

Design Patterns com Delphi : Factory Method

Conforme afirmei no post anterior (Implementando Design Patterns), os Patterns do tipo Criacionais flexibilizam a criação de instâncias de classes (Objetos). No caso do Factory Method, essa flexiblização ocorre ao usar uma interface padrão para criar instâncias de classes que têm relação de herança entre si. A ideia é construir um método (a interface padrão) que é capaz de decidir qual classe deve ser instanciada, tomando por base um ou mais valores passados por parâmetro.

Neste contexto, Interface não tem a ver com o conceito embutido na palavra chave interface do Delphi. Aqui ele se refere à forma com que a função se relaciona com o mundo exterior, isto é, quais parâmetros deverão ser informados por outras partes do programa para se obter a instância desejada. A interface padrão é, então, uma função bem conhecida que centraliza a criação de instâncias para uma família de classes.

Tome, por exemplo, as classes representadas no diagrama abaixo:

Esquema para Factory Method
Há uma classe TWForma que é base para 3 heranças conhecidas (TWCirculo, TWQuadrado e TWTriangulo). Em Delphi, essa relação seria declarada como segue:
type
TWForma = class
{ ... }
procedure Desenha;virtual;
end;
TWCirculo = class(TWForma)
{ ... }
procedure Desenha;override;
end;
TWQuadrado = class(TWForma)
{ ... }
procedure Desenha;override;
end;
TWTriangulo = class(TWForma)
{ ... }
procedure Desenha;override;
end;

A outra classe existente no esquema é a TWFormaFactory. Através dela implementaremos um único método - o Factory Method - que é declarado como um método de classe, isto é, não é preciso instanciar a classe para utilizá-lo. Esse método é o coração do Design Pattern Factory Method pois é nele que fica a inteligência para instanciar as classes. Veja a declaração:
type
TWTipoForma = (tfCirculo, tfQuadrado, tfTriangulo);
TWFormaFactory = class
class function GetTWForma (ATipo: TWTipoForma): TWForma;
end;

implementation

class function TWFormaFactory.GetTWForma (ATipo: TWTipoForma): TWForma;
begin
Result := Nil;
case ATipo Of
tfCirculo: Result := TWCirculo.Create;
tfQuadrado: Result := TWQuadrado.Create;
tfTriangulo: Result := TWTriangulo.Create;
end;
end;

Aqui, a interface padrão para criar instâncias do TWForma é o método GetTWForma. Repare que ele tem conhecimento prévio sobre as classes que serão criadas. Por causa do polimorfismo, o método pode ser declarado de forma a retornar um ponteiro para a classe base TWForma e o resto do programa não precisa se preocupar com qual tipo foi realmente instanciado já que a classe TWForma introduziu acesso a todo o comportamento que é esperado. Isso configura o cenário ideal para usar o padrão Factory Method: classes com relação de herança e conhecimento prévio das classes a instanciar.

Usar esse padrão tem um clara vantagem se comparada a usar simplesmente o constructor de cada classe: se novas heranças de TWForma forem declaradas, haverá um único ponto do código para ser alterado.

Uma forma de usar o exemplo seria incluir um Combo num Form para permitir que o usuário adicione um nova forma a uma lista e essa lista controle o que será desenhado em tela:
tpForma := TWTipoForma (ComboBox1.ItemIndex);
forma := TWFormaFactory.GetTWForma (tpForma);
ListaFormas.Add (forma);
{ ... }
forma.Desenha;

2 comentários :

Blogueiro disse...

Dicas valiosas aqui no seu blog. Muito obrigado por compartilhar o conhecimento.

Anônimo disse...

Parabéns Luis. Seus artigos são nota 10 !!!

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.