C# -  Padrão Factory Method - I


Neste artigo veremos o padrão de projeto Factory Method através de um exemplo prático.

O padrão de projeto Factory Method é um dos padrões de projeto entre os 23 padrões da Gang of Four (GoF) sendo um padrão de design criacional.

Esse padrão oculta a criação dos objetos e permite que os objetos sejam criados em tempo de execução, quando necessário, e, nos permite criar objetos sem expor a lógica de criação.



Este padrão de projeto define uma interface para criar um objeto mas não especifica quais objetos as implementações individuais desta interface irão instanciar.  Ele deixa que as subclasses decidam qual classe instanciar permitindo também que uma classe adie a instanciação para as subclasses.

O diagrama UML deste padrão é fornecido abaixo.

Vamos supor um cenário onde temos uma aplicação que precisa criar diversos tipos de lanches.

Vamos materializar o conceito usando esse exemplo onde vamos usar o método Factory para criar uma variedade de lanches diferentes.

Vamos criar um projeto Console (.NET 5) usando o Visual Studio Community chamado ConsoleApp1.

Os lanches são compostos de ingredientes e vamos definir uma classe abstrata Lanche que faz o papel do participante Product. (Uma classe abstrata não pode ser instanciada e serve como uma classe base.)

   public abstract class Lanche
   {
        public abstract string Nome { get; }
        public ArrayList Ingredientes = new ArrayList();
   }

A seguir vamos definir os produtos concretos (ConcreteProduct) que vão herdar da classe Lanche e implementar os lanches que vamos criar.

A título de exemplo vamos criar  os lanches : Bauru, Frango, MistoQuente e Vegetariano

    public class Bauru : Lanche
    {
        private readonly string _nomeLanche;
        public Bauru()
        {
            _nomeLanche = "Bauru";
            Ingredientes.Add("Pão Francês");
            Ingredientes.Add("Presunto");
            Ingredientes.Add("Mussarela");
            Ingredientes.Add("Tomate e maionese");
        }
        public override string Nome { get => _nomeLanche; }
    }
    public class Frango : Lanche
    {
        private readonly string _nomeLanche;
        public Frango()
        {
            _nomeLanche = "Lanche de Frango";
            Ingredientes.Add("Peito de frango");
            Ingredientes.Add("Maionese e tomate");
        }
        public override string Nome { get => _nomeLanche; }
    }
    public class MistoQuente : Lanche
    {
        private readonly string _nomeLanche;
        public MistoQuente()
        {
            _nomeLanche = "Misto Quente";
            Ingredientes.Add("Pão Francês");
            Ingredientes.Add("Presunto e Mussarela na chapa");
        }
        public override string Nome { get => _nomeLanche; }
    }
    public class Vegetariano : Lanche
    {
        private readonly string _nomeLanche;
        public Vegetariano()
        {
            _nomeLanche = "Lanche Vegetariano";
            Ingredientes.Add("Alface e Rúcula ");
            Ingredientes.Add("Ervilha e Tomate");
        }
        public override string Nome { get => _nomeLanche; }
    }

Todas as classes herdam da classe Lanche a nossa classe base.

Abaixo temos o diagrama de classe desta implementação:

Queremos construir uma fábrica que nos permita construir diferentes tipos de lanches usando. O que vai diferir entre os tipos de lanches será a quantidade e a ordem dos ingredientes.

Vamos então criar a classe abstrata LancheFactoryMethod que representa o Creator e que implementa o método de fábrica que iremos usar para criar os lanches

    public abstract class LancheFactoryMethod
    {
        public abstract Lanche CriarLanche(int tipo);
    }

O método CriarLanche() é o método Factory que dá ao padrão seu nome.

A seguir vamos criar a classe LancheFactory que representa o ConcreteCreator e que herda de LancheFactoryMethod e sobrescreve o método de fábrica e retorna uma instância de um Lanche conforme o tipo selecionado:

using ConsoleApp1.ConcreteProduct;
using ConsoleApp1.Creator;
namespace ConsoleApp1.ConcreteCreator
{
    public class LancheFactory : LancheFactoryMethod
    {
        public override Lanche CriarLanche(int tipo)
        {
            if (tipo == 1)
            {
                return new Bauru();
            }
            else if (tipo == 2)
            {
                return new Frango();
            }
            else if (tipo == 3)
            {
                return new MistoQuente();
            }
            else if (tipo == 4)
            {
                return new Vegetariano();
            }
            else
            {
                throw new System.ArgumentException("Lanche não encontrada");
            }
        }
    }
}

Abaixo temos o diagrama de classes desta implementação:

Pronto, agora para poder criar objetos de cada tipo de lanche vamos definir o código a seguir no método Main que representa o Cliente:

using ConsoleApp1.ConcreteCreator;
using System;
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Escolha o Lanche : ");
            Console.WriteLine("(1)Bauru  (2)Frango  (3)Misto Quente  (4)Vegetariano");
            var lancheEscolhido = Convert.ToInt32(Console.ReadLine());
            try
            {
                LancheFactory factory = new LancheFactory();
                Lanche lanche = factory.CriarLanche(lancheEscolhido);
                Console.WriteLine(lanche.Nome);
                foreach (var ingrediente in lanche.Ingredientes)
                    Console.WriteLine(ingrediente);
                Console.WriteLine("\nLanche montado com sucesso");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Erro :" + ex.Message);
            }
            Console.ReadLine();
        }
    }
}

Resultado:

Usando o padrão Factory Method temos que :

Na próxima parte do artigo veremos como evitar usar os blocos if/else ou switch nas subclasses para criar as instâncias do Produto.

Pegue o código do projeto aqui : FactoryMethod1.zip

Referências:


José Carlos Macoratti