C# - Usando Factory Pattern sem switch/if


Hoje veremos como usar o padrão Factory evitando o uso de instruções switch ou if.

Se você procurar por exemplos usando o padrão Factory, na grande maioria, vai encontrar a implementação da Factory usando as instruções switch ou if/else.

Para mostrar um exemplo dessa implementação vejamos um cenário muito comum onde temos uma classe abstrata Cargo que define um método abstrato Nome:

abstract class Cargo
{
    public abstract string Nome { get; }
}

A seguir temos as classes Gerente, Administrador e Programador que herdam de Cargo e sobrescrevem o método Nome:

class Gerente : Cargo
{
    public override string Nome
    {
        get { return "Gerente"; }
    }
}

class Administrador : Cargo
{
    public override string Nome
    {
        get { return "Administrador"; }
    }
}

class Programador : Cargo
{
    public override string Nome
    {
        get { return "Programador"; }
    }
}

 

A seguir temos uma classe Factory com um método estático Get que retorna um tipo Cargo :

static class Factory
{
    public static Cargo Get(int id)
    {
      switch (id)
      {
        case 0: return new Gerente();
        case 1: return new Administrador();     
        case 2: return new Programador();
        default: return new Programador();
      }
    }
}

Como exemplo de utilização podemos definir um laço for e invocar o método Get() da Factory:

for (int i = 0; i <= 2; i++)
{
    var cargo = Factory.Get(i);
    WriteLine("Quando id = {0}, cargo = {1} ", i, cargo.Nome);     
}
ReadLine();

Temos aqui um exemplo de uso do Factory Method  que usa métodos de fábrica para criar objetos sem ter que especificar a classe do objeto que será criado. (Este método não faz parte do livro Gof)

Nosso problema é eliminar o uso do bloco switch na classe Factory.

Naturalmente não existe apenas uma solução para fazer isso, e aqui, eu vou mostrar uma delas.

Usando um factory sem instruções switch ou if

Vamos iniciar definindo a interface ICargo

public interface ICargo
{
    string Nome { get; }
}

A seguir as classes Gerente, Administrador e Programador implementam esta interface:

class Gerente : ICargo
{
    public string Nome
    {
        get { return "Gerente"; }
    }
}

class Administrador : ICargo
{
    public string Nome
    {
        get { return "Administrador"; }
    }
}

class Programador : ICargo
{
    public string Nome
    {
        get { return "Programador"; }
    }
}

E agora temos a implementação da classe CargoFactory onde agora não estamos usando o bloco switch:

static class CargoFactory
{
    public static T Create<T>() where T : ICargo, new()
    {
        return new T();
    }
}

Nesta classe temos o método estático Create<T> onde temos uma restrição ao parâmetro genérico T que deve ser a interface ICargo e deve ter um construtor padrão sem parâmetro público.

Isso significa que T não pode ser int, float, double, DateTime ou qualquer outra estrutura (um tipo de valor), tem que ser do tipo ICargo e tem que  ter um construtor padrão ou sem parâmetros.

Agora para usar a Factory podemos fazer:

using System;

ICargo cargo1 = CargoFactory.Create<Gerente>();
Console.WriteLine("0: " + cargo1.Nome);

ICargo cargo2 = CargoFactory.Create<Administrador>();
Console.WriteLine("1: " + cargo2.Nome);

ICargo cargo3 = CargoFactory.Create<Programador>();
Console.WriteLine("1: " + cargo3.Nome);

Console.ReadLine();

Para este exemplo temos outra alternativa para o Factory que é criar uma instância da Interface usando o tipo anônimo:

static class CargoFactory
{
    public static ICargo Create(string nomeCargo)
    {
        Type type = Type.GetType(nomeCargo);
        return (ICargo)Activator.CreateInstance(type);
    }
}

Para usar esta Factory podemos fazer assim:


ICargo
cargo4 = CargoFactory.Create("Gerente");
Console.WriteLine(cargo4.Nome);
 

E estamos conversados...

Pegue o projeto aqui:  Factory_SemIfs.zip

"Confessai as vossas culpas uns aos outros, e orai uns pelos outros, para que sareis. A oração feita por um justo pode muito em seus efeitos."
Tiago 5:16

Referências:


José Carlos Macoratti