.NET

14 dez, 2012

.NET – Princípios de programação e padrões de projetos – Parte 02

Publicidade

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:

  1. 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;
  2. A fábrica cria uma instância de um novo produto concreto e retorna ao cliente o produto recém-criado;
  3. 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