Vamos adicionar mais um padrão para nossa caixa de ferramentas, vamos falar sobre o Factory, um padrão que fornece a possibilidade de criarmos uma fabrica para criação dos nossos objetos em tempo de execução, deixando o cliente isento de instanciar a classe ganhando um dinamismo para a aplicação.

Neste artigo vamos criar uma fabrica de robôs, onde teremos três classes concretas de robôs, uma classe abstrata para os robôs e uma fabrica para instanciarmos os robôs na aplicação, todas as classes concretas deverão ser capazes de sobreviver na aplicação de forma independente da fabrica, o resultado que vamos alcançar com a criação da fabrica é podermos instanciar robôs concretos de forma dinâmica, hora instanciaremos uma classe hora outra, sem termos que alterar a tipo do objeto que esta no teste.

Quando temos um grupo de classes concretas relacionadas teríamos que escrever o código assim:

String tipo = "C6PO";

robos robo;

if (tipo.Equals("C6PO"))

    robo = new C6PO();

else if (tipo.Equals("Bumblebee"))

    robo = new Bumblebee();

else if (tipo.Equals("Optimus_Prime"))

    robo = new Optimus_Prime();

Assim temos varias classes sendo instanciadas, e a decisão de qual instanciar é tomada no tempo de execução. Um código assim no meio da sua aplicação pode gerar problemas na hora de adicionar ou remover robôs, e esse tipo de código sempre termina espalhado no seu aplicativo, sendo assim você teria que procurar e analisar todos os locais que você utilizou esse bloco.

Vamos ver como o factory vai nos ajudar nas questões levantadas.

Vamos começar pelos enumeradores, ainda vai ficar um enumerador que iremos criar quando criarmos a fabrica.

public enum EDirecaoAndar

{

    Frente,

    Traz,

    Esquerda,

    Direita

}

 

public enum EGirar

{

    Esquerda,

    Direita

}

 

public enum EModo

{

    veiculo,

    robo

}

Os nossos robôs terão ações e modo, as ações serão Andar, Girar, Transformar e executar Scripts, e os modos são veículo e robô.

Agora criaremos a classe abstrata que será base de nossos robôs.

public abstract class robos

{

    protected String nome { get; set; }

 

    protected EModo Modo { get; set; }

 

    public void andar(EDirecaoAndar Direcao, int passos)

    {

        Console.WriteLine("O robo {0} está andando para {1} {2} passo(s)", this.nome, Direcao.ToString(), passos);

    }

 

    public void Girar(EGirar Movimento, int grau)

    {

        Console.WriteLine("O robo {0} está girando para {1} {2}º", this.nome, Movimento.ToString(), grau);

    }

 

    public abstract void Transformar();

 

    public abstract void Script(int Numero);

 

}

Os métodos Girar e Andar já são implementados na classe base, e os métodos Transformar e Script são abstrata para obrigar as classes que herdam a classe base implementar estes métodos, teremos em mente as seguintes situações:

                O método Transformar vai alterar o modo de veículo para robô e vice e versa;

                O método Script vai ser a chamada para ações em lote e/ou novas ações;

Poderíamos ter criado uma interface ao invés de uma classe abstrata, mas eu queria deixar os métodos comum a todos já implementado, assim ganhando produtividade.

Criaremos agora as classes concretas dos robôs cada um com uma característica diferente e todos herdando da classe abstrata “robos”.

public class C6PO : robos

{

    public C6PO()

    {

        this.nome = "C6PO";

        this.Modo = EModo.robo;

    }

 

    public override void Transformar()

    {

        Console.WriteLine("{0} não tem opção de se transformar!", this.nome);

        //this.Modo = (this.Modo == EModo.veiculo ? EModo.robo : EModo.veiculo);

    }

 

    public override void Script(int Numero)

    {

        this.andar(EDirecaoAndar.Frente, 20);

        this.Girar(EGirar.Esquerda, 90);

        this.andar(EDirecaoAndar.Frente, 100);

        this.Girar(EGirar.Direita, 19);

    }

}

 

public class Optimus_Prime : robos

{

    public Optimus_Prime()

    {

        this.nome = "Optimus_Prime";

        this.Modo = EModo.veiculo;

    }

 

    public override void Transformar()

    {

        this.Modo = (this.Modo == EModo.veiculo ? EModo.robo : EModo.veiculo);

 

        Console.WriteLine("{0} em modo {1}", this.nome, this.Modo.ToString());

    }

 

    public override void Script(int Numero)

    {

        switch (Numero)

        {

            case 1:

                if (this.Modo == EModo.veiculo)

                    this.Transformar();

                this.Girar(EGirar.Esquerda, 25);

                this.andar(EDirecaoAndar.Esquerda, 2);

                this.andar(EDirecaoAndar.Frente, 10);

                break;

        }

 

 

    }

}

 

public class Bumblebee : robos

{

    public Bumblebee()

    {

        this.nome = "Bumblebee";

        this.Modo = EModo.veiculo;

    }

 

    public override void Transformar()

    {

        this.Modo = (this.Modo == EModo.veiculo ? EModo.robo : EModo.veiculo);

 

        Console.WriteLine("{0} em modo {1}", this.nome, this.Modo.ToString());

    }

 

    public override void Script(int Numero)

    {

        switch (Numero)

        {

            case 1:

                if (this.Modo == EModo.veiculo)

                    this.Transformar();

                this.Girar(EGirar.Direita, 1);

                this.andar(EDirecaoAndar.Esquerda, 2);

                this.andar(EDirecaoAndar.Frente, 1);

                break;

            case 2:

                if (this.Modo == EModo.veiculo)

                    this.Transformar();

                this.andar(EDirecaoAndar.Frente, 15);

                this.andar(EDirecaoAndar.Direita, 4);

                this.Girar(EGirar.Direita, 180);

                this.andar(EDirecaoAndar.Frente, 2);

                break;

            case 3:

                if (this.Modo == EModo.veiculo)

                    this.Transformar();

                Console.WriteLine("{0} fez alguns passos de samba.", this.nome);

                break;

        }

 

 

    }

}

Criamos as três classes robôs C6PO, Optimus_Prime e Bumblebee, essas implementa os script, implementa ou não o Transformar.

Neste momento temos os robos que podem ser utilizados em nosso aplicativo perfeitamemente de forma direta e convencional, mas a nossa atenção não é essa...

Agora vamos implementar o conceito do padrão factory, bom já temos a classe abstrata robos que será fundamental para nossa fabrica, pois será o tipo retornado da nossa fabrica. Antes de ir para a fabrica vamos criar mais um enumerador para ser utilizado na factory. 

public enum ERobos

{

    C6PO,

    Optimus_Prime,

    Bumblebee

}

Temos ai os nomes das nossas classes. Entenda que para cada classe concreta que criarmos terá que criar um item neste enumerado. Esse passo não é fundamental, mas para termos uma melhor manipulação na hora de utilizarmos os factory.

E agora vamos criar a nossa fabrica.

public static class Factory

{

    public static robos Criar(ERobos robo)

    {

        return Criar(robo.ToString());

    }

 

    public static robos Criar(String robo)

    {

        switch (robo)

        {

            default:

            case "C6PO":

                return new C6PO();

            case "Optimus_Prime":

                return new Optimus_Prime();

            case "Bumblebee":

                return new Bumblebee();

        }

    }

}

A fabrica é uma classe static com os métodos statics. O método Criar tem uma sobrecarga, podemos passa o nome da classe ou uma opção do enumerador. Essa classe será responsável por instanciar as classes e retornará um objeto do tipo “robos”. Você pode estar pensando, “Esse bloco de código é muito parecido com o primeiro apresentado”, sim, é praticamente o mesmo, a diferença que agora temos uma classe responsável por isso, com isso não vamos espalhar código para decidir qual rodo será instanciado, em case de uma expansão, faremos facilmente a inserção do robô na nossa fabrica, e independentemente de onde estamos utilizando ela teremos um resultado literalmente igual.

Agora testaremos a nossa fabrica.

class teste

{

    static void Main(string[] args)

    {

        List<ERobos> Robos = new List<ERobos> { ERobos.C6PO, ERobos.Bumblebee, ERobos.Optimus_Prime };

 

        robos robo1;

 

        Robos.ForEach(delegate(ERobos R)

        {

            robo1 = Factory.Criar(R);

            robo1.Script(0);

            robo1.Transformar();

            robo1.Script(1);

            robo1.Script(2);

            robo1.Script(3);

            robo1.Transformar();

            Console.WriteLine();

        });

 

        Console.ReadKey();

    }

}

A essência deste teste será, criaremos uma lista dos enumeradores com um de cada opção, temos também o robo1 do tipo robos que recebera a classe criada pelo factory, depois disso vamos fazer um for e colocar a posição da lista para criar os objetos do tipo, e com isso executar os métodos das classes fornecida pela fabrica.

Chegamos ao fim deste artigo, espero novamente ter conseguido passar a idéia desse conceito. Até a próxima, ahh não esqueçam de fazer o download do exemplo.

 

Fontes:

FREEMAN, ERIC & FREEMAN, ELISABETH – Use a Cabeça! Padrões de Projetos (Design Patterns), 2ª Edição [http://www.livrariasaraiva.com.br/produto/1995765/]