Padrões de projeto em .NET: Factory Method
Também conhecido como Virtual Constructor, este padrão tem por objetivo definir uma interface para criar um objeto, mas deixar as subclasses decidirem que classe instanciar. O Factory Method permite adiar a instanciação para subclasses.
Os frameworks usam classes abstratas para definir e manter relacionamentos entre objetos. Um framework é freqüentemente responsável também pela criação desses objetos.
Considere um framework para aplicações que possuem um cadastro de clientes. Duas abstrações-chave nesse framework são as classes Sistema e Cadastro. As duas classes são abstratas, e os clientes devem prover subclasses para realizar suas implementações específicas para a aplicação. Por exemplo, para criar uma aplicação para uma mecânica, definimos as classes SistemaMecanica e CadastroMecanica. A classe Sistema é responsável pela administração de Cadastros e irá criá-los conforme exigido – quando o usuário seleciona Consultar ou Novo, por exemplo, em um menu.
Uma vez que a subclasse Cadastro a ser instanciada é própria da aplicação específica, a classe Sistema não pode prever a subclasse de Cadastro a ser instanciada – a classe Sistema somente sabe quando um cadastro e não que tipo de Cadastro criar. Isso cria um dilema: o framework deve instanciar classes, mas ele somente tem conhecimento de classes abstratas, as quais não pode instanciar.
O padrão Factory Method oferece uma solução. Ele encapsula o conhecimento sobre a subclasse de Cadastro que deve ser criada e move este conhecimento para fora do framework.
As subclasses de Sistema redefinem uma operação abstrata CriarCadastro em Sistema para retornar a subclasse apropriada de Cadastro. Uma vez que uma subclasse de Sistema é instanciada, pode, então, instanciar Cadastros específicos da aplicação sem conhecer suas classes. Chamamos CriarCadastro um factory method porque ele é responsável pela “manufatura” de um objeto.
Quando usar Factory Method?
Use o padrão Factory Method quando:
- Uma classe não pode antecipar a classe de objetos que criam;
- Uma classe quer que suas subclasses especifiquem os objetos que criam;
- As classes delegam responsabilidade para uma dentre várias subclasses auxiliares, e você quer localizar o conhecimento de qual subclasse auxiliar que é a delegada
Estrutura
- Produto (Cadastro): define a interface de objetos que o método fábrica cria.
- ProdutoConcreto (MeuCadastro): implementa a interface de Produto.
- Criador (Sistema): declara o método fábrica, o qual retorna um objeto do tipo Produto. Criador também pode definir uma implementação por omissão do método factory que retorna por omissão um objeto ProdutoConcreto.
- CriadorConcreto (MeuSistema): redefine o método-fábrica para retornar a uma instância de um ProdutoConcreto.
Exemplo de código
Existem duas variedades principais para o padrão Factory Method. Na primeira, a classe Criador é uma classe abstrata e não fornece uma implementação para o método-fábrica que ela declara. Na segunda, Criador é uma classe concreta e fornece uma implementação por omissão para o método-fábrica. Também é possível ter uma classe abstrata que define uma implementação por omissão, mas isto é menos comum.
O primeiro caso exige subclasses para definir uma implementação porque não existe uma omissão razoável, assim contornando o dilema de ter que instanciar classes imprevisíveis. No segundo caso, o CriadorConcreto usa o método fábrica principalmente por razões de flexibilidade. Está seguindo uma regra que diz: “criar objetos numa operação separada de modo que subclasses possam redefinir a maneira como eles são criados”. Essa regra garante que projetistas de subclasses, caso necessário, possam mudar a classe de objetos que a classe ancestral instancia.
Vejamos, então, um exemplo para o segundo caso, em que o CriadorConcreto tem liberdade para definir o método-fábrica.
Public Interface Sistema
Function Criar(ByVal CadastroID As String) As Cadastro
End Interface
Public Class SistemaMecanica
Implements Sistema
Public Function Criar(ByVal CadastroID As String) As Cadastro _
Implements Sistema.Criar
If CadastroID.Trim.ToUpper.Equals("MECANICA") Then
Return New CadastroMecanica
ElseIf CadastroID.Trim.ToUpper.Equals("PADARIA") Then
Return New CadastroPadaria
End If
End Function
End Class
No próximo artigo estaremos discutindo outro padrão de criação: Prototype