C# - Padrão Abstract Factory
Neste artigo veremos o padrão de projeto Abstract Factory através de um exemplo prático. |
O padrão de projeto Abstract Factory é um dos padrões de projeto entre os 23 padrões da Gang of Four (GoF) sendo um padrão de design criacional.
Este padrão permite a criação de famílias de objetos relacionados ou dependentes por meio de uma única interface e sem que a classe concreta seja especificada.
O objetivo em empregar o padrão é isolar a criação de objetos de seu uso e criar famílias de objetos relacionados sem ter que depender de suas classes concretas. O uso deste padrão torna possível trocar implementações concretas sem alterar o código que estas usam, mesmo em tempo de execuçã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.
AbstractFactory – Declara métodos para criar um AbstractProduct que são implementados por ConcreteFactory que estende AbstractFactory;
ConcreteFactory – Implementa métodos de AbstractFactory para criar objetos do tipo Product que são retornados como um AbstractProduct;
AbstractProduct – Declara métodos que são implementados por Product. Cria um objeto do tipo Product mas retorna AbstractProduct ;
Product – Estende AbstractProduct e implementa seus métodos criando produtos concretos;
A fábrica determina o tipo concreto do objeto a ser criado, e é nela que o objeto é realmente criado. No entanto, a fábrica só retorna um ponteiro abstrato para o objeto concreto criado. (wikipédia)
O código do cliente não tem conhecimento algum do tipo concreto e os objetos concretos são, de fato criados pela fábrica, mas o código do cliente acessa tais objetos só através da sua interface abstrata. (wikipédia)
Este padrão é uma extensão do padrão de factory method, que permite criar objetos sem se preocupar com a classe real do objeto que está sendo criado.
Quando usar este padrão ?
Use este padrão quando :
Você deseja criar um conjunto de objetos relacionados ou objetos dependentes que devem ser usados juntos;
O sistema deve ser configurado para funcionar com várias famílias de produtos;
As classes concretas devem ser desacopladas dos clientes;
Para ilustrar a aplicação deste padrão de projetos vamos imaginar que precisamos criar objetos do tipo Pizza e objetos do tipo Bolo. Temos assim duas família de produtos que embora diferentes são feitos da mesma substância : a massa.
Vamos criar um projeto Console (.NET 5) usando o Visual Studio Community chamado ConsoleApp1.
Vamos definir que queremos criar Pizza de Mussarela e Calabreza e bolo de Chocolate e Laranja. Assim vamos criar agora 3 enumerações para definir os tipos com os quais vamos tratar : TipoMassa, TipoPizza e TipoBolo
public enum TipoMassa { Pizza = 0, Bolo = 1, } |
public enum TipoPizza { Mussarela = 0, Calabreza = 1 } |
public enum TipoBolo { Chocolate = 0, Laranja = 1 } |
Como temos duas famílias de objetos que desejamos criar, Pizza e Bolo, vamos criar uma classe abstrata MassaBase da qual Bolo e Pizza vão herdar:
using System.Collections;
namespace AbstractFactory1 public ArrayList Ingredientes = new ArrayList(); public MassaBase(string nome, TipoMassa tipo) |
A seguir vamos definir as classes abstratas Bolo e Pizza que representam as famílias de objetos para a qual desejamos criar objetos e que herdam da classe MassaBase:
//AbstractProductA public abstract class Bolo : MassaBase { public Bolo(string nome, TipoMassa tipo) : base(nome, tipo) { } } |
//AbstractProductB public abstract class Pizza : MassaBase { public Pizza(string nome, TipoMassa tipo) : base(nome, tipo) { } } |
Agora podemos criar as classes concretas para PizzaCalabreza, PizzaMussarela, BoloChocolate e BoloLaranja que são os produtos concretos que iremos criar:
//ProductB1 public sealed class PizzaCalabreza : Pizza { public PizzaCalabreza() : base("Pizza Calabreza", TipoMassa.Pizza) { Ingredientes.Add("Calabreza em cubos e tomates em cubos"); } } |
//ProductB2 public sealed class PizzaMussarela : Pizza { public PizzaMussarela() : base("Pizza Mussarela", TipoMassa.Pizza) { Ingredientes.Add("Queijo mussarela gratinado e molho de tomate"); } } |
//ProductA1 public sealed class BoloChocolate : Bolo { public BoloChocolate() : base("Bolo de Chocolate", TipoMassa.Bolo) { Ingredientes.Add("Com cobertura de chocolate Nestlé"); } } |
//ProductA2 public sealed class BoloLaranja : Bolo { public BoloLaranja() : base("Bolo de Laranja", TipoMassa.Bolo) { Ingredientes.Add("Com cobertura de calda de laranja"); } } |
Abaixo temos o diagrama de classe desta implementação:
Vamos agora criar a classe abstrata MassasAbstractFactory onde vamos definir um método que vai ser do tipo de uma interface IMassaFactoryMethod que com base no tipo de massa : bolo ou pizza, vai criar uma instância da fábrica que sabe criar pizzas e bolos.
Assim precisamos criar antes a interface IMassaFactoryMethod que é um factory method e que com base no tipo da massa cria a respectiva fábrica:
using System;
namespace AbstractFactory1.FactoryMethod |
A seguir temos o código da classe MassasAbstractFactory:
using AbstractFactory1.FactoryMethod; using System; namespace AbstractFactory1 |
Agora vamos criar as classes concretas para PizzaFactory e BoloFactory que irão retornar instâncias das classes concretas dos bolos e das pizzas:
using AbstractFactory1.Enums; using AbstractFactory1.FactoryMethod; using System;
namespace AbstractFactory1
switch (tipoBolo) |
using AbstractFactory1.Enums; using AbstractFactory1.FactoryMethod; using System; namespace AbstractFactory1 switch (tipoPizza) |
Abaixo temos o diagrama de classes desta implementação:
Pronto, agora para poder criar objetos de cada tipo de massa e para isso vamos definir o código a seguir no método Main que representa o Cliente:
using AbstractFactory1.Enums; using System; namespace AbstractFactory1 //cria os objetos com base no tipo : bolo //cria os objetos com base no tipo : pizza //exibe os detalhes Console.ReadLine(); private static void ExibeDetalhes(MassaBase massaBase) |
Resultado:
O padrão Abstract Factory apresenta as seguintes vantagens/desvantagens:
Pegue o código do projeto aqui : AbstractFactory1.zip
"Se o SENHOR não edificar a casa, em vão trabalham os que
a edificam; se o SENHOR não guardar a cidade, em vão vigia a sentinela."
Salmos 127:1
Referências:
NET - Unit of Work - Padrão Unidade de ...
NET - O padrão de projeto Decorator
NET - Padrão de Projeto Builder
C# - O Padrão Strategy (revisitado)
NET - O padrão de projeto Command
NET - Apresentando o padrão Repository