Padrão de Projeto: Abstract Factory

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog. Você receberá um email de confirmação. Somente depois de confirma-lo é que poderei lhe enviar os conteúdos exclusivos.

Email inválido.
Blog /Android /Padrão de Projeto: Abstract Factory

Padrão de Projeto: Abstract Factory

Vinícius Thiengo
(4248) (1)
Go-ahead
"Concentre todos seus pensamentos no trabalho que tem em mãos. Os raios solares não queimam até que sejam colocados em foco."
Alexander Graham Bell
Kotlin Android
Capa do livro Desenvolvedor Kotlin Android - Bibliotecas para o dia a dia
TítuloDesenvolvedor Kotlin Android - Bibliotecas para o dia a dia
CategoriasAndroid, Kotlin
AutorVinícius Thiengo
Edição
Capítulos19
Páginas1035
Acessar Livro
Treinamento Oficial
Android: Prototipagem Profissional de Aplicativos
CursoAndroid: Prototipagem Profissional de Aplicativos
CategoriaAndroid
InstrutorVinícius Thiengo
NívelTodos os níveis
Vídeo aulas186
PlataformaUdemy
Acessar Curso
Receitas Android
Capa do livro Receitas Para Desenvolvedores Android
TítuloReceitas Para Desenvolvedores Android
CategoriaDesenvolvimento Android
AutorVinícius Thiengo
Edição
Ano2017
Capítulos20
Páginas936
Acessar Livro
Código Limpo
Capa do livro Refatorando Para Programas Limpos
TítuloRefatorando Para Programas Limpos
CategoriaEngenharia de Software
AutorVinícius Thiengo
Edição
Ano2017
Capítulos46
Páginas599
Acessar Livro
Quer aprender a programar para Android? Acesse abaixo o curso gratuito no Blog.
Conteúdo Exclusivo
Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Opa, blz?

Nesse artigo vamos estudar o padrão Abstract Factory, esse que é uma versão das três possíveis no contexto dos padrões factories.

Recomendo que antes de prosseguir com o estudo do padrão proposto aqui, que você conheça primeiro o padrão Simples Factory, para melhor aproveitar o conteúdo aqui. Mesmo sabendo que os artigos de padrões de projeto e alguns de métodos de refatoração serem independentes de conhecimento prévio de outros artigos aqui do blog.

Tópicos presentes no artigo:

Apresentação

O Abstract Factory tem o mesmo objetivo principal das outras duas versões, remover código de criação (aquele com a palavra reservada new) de nossas classes de negócio.

Porém o Abstract Factory é muito similar ao Factory Method, alias ele tem na composição dele a implementação de vários Factory Method. Por que isso? Porque o Abstract Factory trabalha com a criação de famílias de objetos.

Ok, agora ficou confuso. Eu sei. Com o decorrer do conteúdo ficará claro, o padrão proposto aqui é bem simples de entender.

Diagrama

Segue diagrama do padrão:

Veja a real diferença entre o Abstract Factory e o Factory Method. Uma família de métodos de criação em Abstract Factory. Outra diferença que é notável é que enquanto Factory Method é comumente utilizado como herança o Abstract Factory é comum como composição.

Código de exemplo

Se acompanhou os artigos do Simple Factory e do Factory Method, você já deve estar familiarizado com o projeto de pizzaria, certo? Aqui vamos continuar utilizando ele como projeto de exemplo. Começando com o código da classe Pizzaria:

public class Pizzaria {
private Pizza pizza;

public void criarPizza( String cidade, String tipo ){

if( tipo.equals("queijo") ){
pizza = new PizzaQuatroQueijos( cidade );
}
else if( tipo.equals("portuguesa") ){
pizza = new PizzaPortuguesa( cidade );
}
else if( tipo.equals("calabresa") ){
pizza = new PizzaCalabresa( cidade );
}

pizza.prepara();
pizza.assa();
pizza.empacota();
}
...
}

 

Ok, ainda nada muito sério que um Simple Factory não possa resolver. O código acima, como no artigo sobre o Factory Method, também permite a criação de pizzas para a cidade de São Paulo ou para a cidade de Rio de Janeiro.

Porém para manter o padrão de qualidade no processo de montagem das pizzas, elas têm exatamente o mesmo modo de preparo, somente os ingredientes que mudam. Vamos visualizar isso mais de perto. Segue código da classe PizzaQuatroQueijos:

public class PizzaQuatroQueijos extends Pizza {
private String cidade;
private Massa massa;
private Queijo queijo;
private Molho molho;
private Salsa salsa;

public PizzaQuatroQueijos( String cidade ){
this.cidade = cidade;
}

public void prepara(){

if( cidade.equals("sao-paulo") ){
massa = new MassaGrossa();
queijo = new QueijoMinas();
molho = new MolhoMarroquino();
salsa = new SalsaNobre();
}
else{
massa = new MassaFina();
queijo = new QueijoParmesao();
molho = new MolhoDaRoca();
salsa = new SalsaNobreApimentada();
}
}
...
}

 

Para um melhor entendimento: o queijo utilizado em todas as pizzas que precisam de queijo, na filial de São Paulo, é sempre o QueijoMinas, a massa, se necessária, sempre é MassaGrossa e assim por diante.

Resumo até aqui: o modo de preparo da pizza de quatro queijos de São Paulo e o modo de preparo da pizza de quatro queijos do Rio de Janeiro são exatamente os mesmos, somente os ingredientes é que mudam. Isso é válido para todos os outros sabores.

O que notamos?

Diferente de ter apenas uma pizza ou paulista ou carioca para cada sabor, ou seja, algo que seria facilmente atendido por um Factory Method. Temos uma família de objetos quando é uma pizza paulista e uma outra família quando é uma carioca.

Agora podemos partir para a solução. Vamos a nossa classe abstrata que representa a interface de implementação do padrão Abstract Factory, PizzaIngredientesFactory:

public abstract class PizzaIngredientesFactory {
public abstract Massa criarMassa();

public abstract Queijo criarQueijo();

public abstract Molho criarMolho();

public abstract Salsa criarSalsa();
}

 

Interface de implementação? Isso é uma classe, não?

Bem, não é essa "Interface" que estamos falando e sim o termo interface de modo geral onde uma classe concreta, abstrata ou até mesmo uma Interface (estrutura de linguagem) podem ser sinônimos desse termo. Essas fornecem as definições dos métodos que as subclasses devem trabalhar, implementar.

Agora como fábricas concretas teremos uma factory para São Paulo e outra para Rio de Janeiro. Começando com a SPPizzaIngredientesFactory:

public class SPPizzaIngredientesFactory extends PizzaIngredientesFactory {
@Override
public Massa criarMassa() {
return new MassaGrossa();
}

@Override
public Queijo criarQueijo() {
return new QueijoMinas();
}

@Override
public Molho criarMolho() {
return new MolhoMarroquino();
}

@Override
public Salsa criarSalsa() {
return new SalsaNobre();
}
}

 

E então a RJPizzaIngredientesFactory:

public class RJPizzaIngredientesFactory extends PizzaIngredientesFactory {
@Override
public Massa criarMassa() {
return new MassaFina();
}

@Override
public Queijo criarQueijo() {
return new QueijoParmesao();
}

@Override
public Molho criarMolho() {
return new MolhoDaRoca();
}

@Override
public Salsa criarSalsa() {
return new SalsaNobreApimentada();
}
}

 

Agora nossas fábricas já podem ser utilizadas nas subclasses de Pizza. Vamos passá-las como dependências nos construtores dessas subclasses de Pizza. Como exemplo vamos ao código da classe PizzaQuatroQueijos:

public class PizzaQuatroQueijos extends Pizza {
private PizzaIngredientesFactory ingredientes;
private Massa massa;
private Queijo queijo;
private Molho molho;
private Salsa salsa;

public PizzaQuatroQueijos( PizzaIngredientesFactory ingredientes ){
this.ingredientes = ingredientes;
}

public void prepara(){
massa = ingredientes.criarMassa();
queijo = ingredientes.criarQueijo();
molho = ingredientes.criarMolho();
salsa = ingredientes.criarSalsa();
}
}

 

Veja o código acima e recorde de quando falamos sobre Abstract Factories trabalharem mais como composição ao invés de herança. O exemplo acima é perfeito para esse entendimento, a classe PizzaIngredientesFactory é o tipo de uma variável de instância.

Agora podemos voltar ao código de Pizzaria, porém com a versão mais atual, utilizando as fábricas de ingredientes:

public class Pizzaria {
private Pizza pizza;

public void criarPizza( String cidade, String tipo ){

if( tipo.equals("queijo") ){
pizza = new PizzaQuatroQueijos( getIngredientes(cidade) );
}
else if( tipo.equals("portuguesa") ){
pizza = new PizzaPortuguesa( getIngredientes(cidade) );
}
else if( tipo.equals("calabresa") ){
pizza = new PizzaCalabresa( getIngredientes(cidade) );
}
}

private PizzaIngredientesFactory getIngredientes( String cidade ){
if( cidade.equals("sao-paulo") ){
return new SPPizzaIngredientesFactory();
}
return new RJPizzaIngredientesFactory();
}
...
}

 

Com isso construímos um código mais passivo a evolução quando comparado com o código inicial, pois conseguimos com ele respeitar alguns princípios de orientação a objetos. Bom, Será que respeitamos mesmo?

É... na verdade ainda tem coisa para melhorar. Mas o padrão Abstract Factory já está implementado, utilizando somente ele muito provavelmente não vamos conseguir nada mais eficiente. Veja no código de PizzaQuatroQueijos como as subclasses de Pizza ficam mais intuitivas. O acoplamento com o objeto da cidade é zero, ou seja, poderíamos adicionar Vitória (capital do Espírito Santo) e mesmo assim continuar com as mesmas classes de Pizza sem alterações.

Porém o código de Pizzaria ainda pode ser melhorado, mais precisamente ainda é possível implementar o Factory Method para remover o código de criação de Pizzaria.

Estudando o artigo sobre o Factory Method será tranquila a melhora do código de Pizzaria, digo, abstrair ainda mais o código dessa classe utilizando esse padrão. Deixo essa tarefa com você. Lembre que prática leva a perfeição.

Ponto negativo

  • Implementar um Abstract Factory quando um simples Simple Factory ou Factory Method seriam o suficiente, pode piorar a performance do projeto, ainda mais na leitura dele.

Pontos positivos

  • Como com todos os outros padrões de projeto, utilizar o Abstract Factory implica também em adicionar, ao menos em parte do projeto, uma linguagem universal aos programadores. Padrões tendem a serem conhecidos por muitos developers e na leitura do código a identificação de uso de um padrão já alivia na necessidade de ter de destrinchar o código para entendê-lo;
  • Encapsulamento do código de criação em pontos únicos do projeto removendo também com isso o forte acoplamento entre classes que não deveriam ter um alto nível de relacionamento umas com as outras;

Conclusão

Mesmo com o projeto utilizado aqui sendo também de pizzaria como nos artigos dos outros factories, o contexto é bem distinto, o número de entidades envolvidas é bem maior. Logo nesse caso o Abstract Factory foi a melhor opção.

Sendo assim, se você tem um projeto que trabalha com a criação de famílias de classes e essas instanciações estão em classes que não deveriam ter esse tipo de acoplamento, nesse caso o Abstract Factory provavelmente seria uma excelente escolha para melhora de código.

Ressaltando que não há uma versão melhor do Factory, cada uma se encaixa de maneira mais otimizada em problemas específicos de projeto de software. E isso vale para qualquer padrão de projeto.

Desafio

Como desafio faça uma versão da Abstract Factory acima utilizando ela como uma Interface ao invés de uma classe abstrata. Com Interface é até mais eficiente em termos de recursos de linguagem, pois podemos estender apenas uma classe, já a implementação de Interfaces não tem limites.

Fontes

Use a Cabeça! Padrões de Projetos

Vlw.

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Relacionado

Refatoração de Código: Introduzir Criação Polimórfica com Factory MethodRefatoração de Código: Introduzir Criação Polimórfica com Factory MethodAndroid
Refatoração de Código: Substituir Lógica Condicional Por StrategyRefatoração de Código: Substituir Lógica Condicional Por StrategyAndroid
Refatoração de Código: Formar Template MethodRefatoração de Código: Formar Template MethodAndroid
Refatoração de Código: Compor MethodRefatoração de Código: Compor MethodAndroid

Compartilhar

Comentários Facebook (1)

Comentários Blog

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...