Na primeira parte do artigo eu apresentei alguns conceitos básicos sobre os princípios SOLID e sobre os padrões de projeto. Vamos, agora, partir para a prática e mostrar os conceitos e a implementação dos principais padrões de projeto usando a linguagem C#.
Vou iniciar com o padrão Factory.
1. Padrão de projeto Factory (Fábrica)
É um padrão Criacional e define uma interface para criar um objeto, mas deixa as subclasses decidirem que classe instanciar (Gamma, pag. 104).
Obs: O método Factory permite que uma classe adie a instanciação para as subclasses.
Este padrão é usado para substituir um construtor de classe(s), permitindo que o tipo de objeto a ser instanciado seja determinado em tempo de execução, sendo usado para controlar uma instanciação de classe. O uso deste padrão de projeto reduz o acoplamento entre classes e oferece muito mais flexibilidade no futuro, se os requisitos de sua aplicação mudarem.
Um benefício do padrão de Factory é permitir ao cliente focar no seu papel na arquitetura, porque permite a separação da criação e instanciação de objetos a partir do cliente.
Existem inúmeras variações do padrão Factory, mas em geral sempre temos um modelo básico que envolve os mesmos atores principais: um cliente, uma fábrica, e um produto.
- O cliente é um objeto que requer uma instância de outro objeto (o produto) para algum propósito. Ao invés de criar a instância do produto diretamente, o cliente delega esta responsabilidade para a fábrica;
- Uma vez invocada, a fábrica cria uma nova instância do produto, passando-o de volta para o cliente. Simplificando, o cliente usa a fábrica para criar uma instância do produto;
- A figura abaixo mostra a relação lógica entre estes elementos do padrão:
Com o padrão Factory, temos uma maneira simples e limpa para instanciar, no momento da implementação, um objeto desconhecido. A única coisa que precisamos conhecer é a interface do objeto com um conjunto de propriedades predefinidos e métodos que precisamos usar.
Implementação
Existem diversas maneiras de implementar o padrão Factory (Fábrica). A seguir, temos um modelo básico de implementação que, creio eu, seja uma dos mais simples:
- O cliente precisa de um produto, mas em vez de criá-lo diretamente, usando o operador new, ele pede para a fábrica criar um novo produto, fornecendo as informações sobre o tipo de objeto que ele precisa;
- A fábrica cria uma instância de um novo produto concreto e retorna ao cliente o produto recém-criado;
- O cliente usa os produtos, sem conhecer nada sobre a sua implementação concreta.
Com base neste modelo, vamos criar um novo projeto usando o Visual C# 2010 Express Edition do tipo Console Application com nome PadraoFactorySimples. Vamos criar uma classe abstrata chamada Cargo, que representa o cargo do funcionário na empresa (esta classe representa o produto) e que define o método Nome(). Esta classe é uma classe abstrata e não pode ser instanciada, servindo como classe base para a criação de classes concretas:
abstract class Cargo { public abstract string Nome { get; } }
A seguir, vamos criar três classes: Gerente, Analista e Programador, essa última implementa a classe abstrata Cargo, sobrescrevendo o método Nome da classe Cargo, retornando o nome do cargo:
class Gerente : Cargo { public override string Nome { get{return "Gerente";} } } class Analista : Cargo { public override string Nome { get{return "Analista";} } } class Programador : Cargo { public override string Nome { get{return "Programador";} } }
Agora, vamos criar a classe Factory que representa a fábrica e que será responsável pela criação das instâncias dos objetos das classes concretas, criadas anteriormente:
static class Factory { /// <summary> /// Decide qual classe instanciar /// </summary> public static Cargo Get(int id) { switch (id) { case 0: return new Gerente(); case 1: case 2: return new Analista(); case 3: default: return new Programador(); } } }
No método Main(), da classe Program, vamos utilizar a fábrica para criar as instâncias das classes concretas, conforme o objeto desejado. No caso, usamos um laço for/next para invocar o método Get() da classe Factory, passando o id do objeto desejado:
class Program { static void Main(string[] args) { Console.WriteLine("Implementação do padrão Factory "); Console.WriteLine("--------------------------------"); for (int i = 0; i <= 3; i++) { var cargo = Factory.Get(i); Console.WriteLine("Quando id = {0}, cargo = {1} ", i, cargo.Nome); } Console.ReadKey(); } }
Esta implementação é bem simples, mas apresenta o seguinte problema:
- A inclusão de um novo produto (um novo cargo) fará com que a classe Factory tenha que ser alterada;
- Ocorre a violação do princípio Open/Closed (você deve ser capaz de estender um comportamento de uma entidade de software (classe), sem modificá-la).
Uma variação desta implementação para contornar o problema acima é vista na figura abaixo, onde podemos criar uma fábrica abstrata e a partir dela criar tantas classes concretas quanto forem necessárias:
Vamos implementar esta variação criando uma fábrica abstrata que defina o método CriaCargos() que irá gerar as instâncias dos objetos concretos:
abstract class Fabrica { private ArrayList cargos = new ArrayList(); // Construtor chama a Fábrica public Fabrica() { this.CriaCargos(); } public ArrayList Cargos { get { return cargos; } } // Fábrica public abstract void CriaCargos(); }
A seguir, vamos criar as fábricas concretas que implementam a fábrica abstrata e que serão responsáveis pela criação dos objetos. As fábricas concretas sobrescrevem o método CriarCargos() e retornam os objetos para o cliente:
class Administracao : Fabrica { // Implementação da Fábrica public override void CriaCargos() { Cargos.Add(new Gerente()); } } class Tecnologia : Fabrica { // Implementação da Fábrica public override void CriaCargos() { Cargos.Add(new Analista()); Cargos.Add(new Programador()); } }
A utilização da fábrica abstrata é feita no método Main() da classe Program(), que cria as instâncias das fábricas concretas e gera os objetos desejados:
static void Main(string[] args) { //Os construtores chamam a Fábrica Fabrica[] fabricantes = new Fabrica[2]; fabricantes[0] = new Administracao(); fabricantes[1] = new Tecnologia(); Console.WriteLine("Implementação do padrão Abstract Factory "); Console.WriteLine("-----------------------------------------"); foreach (Fabrica fabricante in fabricantes) { Console.WriteLine("\n" + fabricante.GetType().Name + "--"); foreach (Cargo cargo in fabricante.Cargos) { Console.WriteLine(" " + cargo.GetType().Name); } } Console.ReadKey(); }
Pegue o projeto completo aqui: PadraoFactorySimples.zip