Padrão de Projeto: Simple 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: Simple Factory

Padrão de Projeto: Simple Factory

Vinícius Thiengo
(3527)
Go-ahead
"Toda realização de valor, grande ou pequena, tem suas etapas de trabalho penoso e triunfo: um começo, uma luta e uma vitória."
Mahatma Gandhi
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
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 abordar um dos três tipos de padrões Factory, mais precisamente o padrão Simple Factory. Esse padrão é comumente apresentado junto aos outros dois tipos de Factory: Method e Abstract. Porém aqui no Blog foi escolhido separa-los para melhorar o entendimento, um artigo para cada um deles.

Tópicos presentes no artigo:

Apresentação

O Simple Factory, por ser mais simples, vem primeiro nos estudos. Logo depois é recomendada a leitura dos artigos posteriores na seguinte ordem: Factory Method e então o Abstract Factory.

Antes de prosseguir já com o conteúdo do Simple Factory, saiba que em orientação a objetos (OO) temos princípios de programação que se seguidos nos permitem obter o melhor de nossos softwares OO, mais precisamente nos permitem evoluí-los correta e rapidamente.

Os padrões Factory aplicam alguns desses princípios, o princípio que é percebido com maior facilidade é o: "Princípio da inversão de dependência". Que basicamente nos diz para dependermos de abstração e não de classes concretas

O que você está querendo dizer com isso?

Veja o código abaixo:

public class Pizzaria {
private Pizza pizza;

public void criarPizza( String tipo ){

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

 

A palavra chave new cria um forte acoplamento entre a classe Pizzaria e as subclasses de Pizza.

Ok, mas o que há de ruim nisso?

O principal problema, e suficiente para a aplicação do padrão, é que outras partes de seu programa de pizzaria podem precisar de instanciar subclasses de Pizza, logo você terá de repetir o código de criação (new PizzaQualquer()) ou trabalhar com um objeto do tipo Pizzaria para obter um dos tipos de pizza mesmo quando somente uma simples criação de objetos Pizza já seria o suficiente.

Um outro problema é que se você precisar modificar algo no script de criação de Pizza, por exemplo: não atender mais a demanda de pizzas de calabresa devido ao alto custo e ao baixo lucro. Nesse contexto você terá de percorrer todo o código do projeto buscando por códigos que têm a criação direta de instâncias de pizzas de calabresa (new PizzaCalabresa()) para removê-los. Isso acontecerá caso você tenha optado por não utilizar o modelo convencional de instanciação de Pizza (por meio de uma classe Pizzaria) e sim diretamente optado pela criação via new. Tudo devido ao problema relatado no parágrafo anterior.

Um terceiro e não menos importante problema é que o código acima está quebrando não somente o princípio de "Inversão de dependência" mas também alguns outros princípios de orientação a objetos, como:

  • "Programe para interface ao invés de para implementação”;
  • "Prefira ligações leves entre objetos que se relacionam”;
  • "Classes devem estar fechadas para modificação, mas abertas para extensão”.

Esses são alguns dos problemas do código de Pizzaria.

Uma opção para remover o script de criação dessa classe e consequentemente reutilizar esse script sem violar os princípios citados é utilizando o padrão Simple Factory.

Diagrama

A seguir o diagrama do padrão proposto nesse artigo:

Simples, não? Apesar de parecer que nossa classe Pizzaria já faz isso, digo, segue o padrão Simple Factory, não assuma isso como verdade, pois o recomendado é ter a classe Factory somente para criação dos objetos de contexto dela. A classe Pizzaria tem outros métodos de contexto, além do método de criação de instâncias.

Vamos seguir com o código de exemplo, pois muito provavelmente a aplicação do SimpleFactory ainda não ficou clara somente com os texto e diagrama apresentados até aqui.

Código de exemplo

Abaixo o algoritmo da nova classe Factory adicionada ao projeto, mais precisamente a classe PizzaFactory:

public class PizzaFactory {

public Pizza criarPizza( String tipo ){
Pizza pizza = null;
if( tipo.equals("queijo") ){
pizza = new PizzaQuatroQueijos();
}
else if( tipo.equals("portuguesa") ){
pizza = new PizzaPortuguesa();
}
else if( tipo.equals("calabresa") ){
pizza = new PizzaCalabresa();
}
else if( tipo.equals("camarao") ){
pizza = new PizzaCamarao();
}
return pizza;
}
}

 

Note que quando trabalhando com o Simple Factory nossa classe Factory já é uma Concrete Factory (fábrica concreta).

Concrete Factory?

Sim, não há interface abstrata necessitando de herança ou implementação de Interface para criar as classes que vão ser as fábricas de objetos. Algo comum em Factory Method e Abstract Factory.

Ok, acho que entendi, agora é somente colocar a instância de PizzaFactory em Pizzaria, certo?

Correto. Segue código atualizado de Pizzaria:

public class Pizzaria {
private Pizza pizza;

public void criarPizza( String tipo ){

PizzaFactory pizzaFactory = new PizzaFactory();
pizza = pizzaFactory.criarPizza( tipo );
}
...
}

 

Somente essa alteração já nos dá um grande ganho para a evolução do projeto como um todo. Provavelmente você já deve ter visto uma implementação desse padrão utilizando entidades como static, certo?

Ok, nos códigos abaixo vamos trabalhar com a versão estática desse padrão (que para falar a verdade, pouca coisa muda). Primeiro a classe PizzaFactory:

public class PizzaFactory {

public static Pizza criarPizza( String tipo ){

Pizza pizza = null;
if( tipo.equals("queijo") ){
pizza = new PizzaQuatroQueijos();
}
else if( tipo.equals("portuguesa") ){
pizza = new PizzaPortuguesa();
}
else if( tipo.equals("calabresa") ){
pizza = new PizzaCalabresa();
}
else if( tipo.equals("camarao") ){
pizza = new PizzaCamarao();
}
return pizza;
}
}

 

Note o static na assinatura do método criarPizza(). Agora partimos para a classe Pizzaria:

public class Pizzaria {
private Pizza pizza;

public void criarPizza( String tipo ){
pizza = PizzaFactory.criarPizza( tipo );
}
...
}

 

E você ainda pode querer em sua lógica de negócio a certeza da não instanciação da classe PizzaFactory e nem mesmo que a herança seja trabalhada a partir dela. Nesse cenário deveríamos utilizar as palavras-chave final e private respectivamente na assinatura da classe e no construtor padrão da classe. Segue código atualizado:

public final class PizzaFactory {

private PizzaFactory(){}

public static Pizza criarPizza( String tipo ){

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

return pizza;
}
}

 

Com isso finalizamos nosso exemplo do padrão Simple Factory e tenho quase certeza que você deve estar se perguntando: Por que a variável pizza de PizzaFactory não é uma variável de instância como em Pizzaria?

Em PizzaFactory temos o uso de subclasses de Pizza somente dentro do método criarPizza(), logo não há necessidade de manter uma variável de instância para isso e tenha em mente que quanto menor o escopo melhor para atualizar, evoluir, ficará seu software.

Em Pizzaria muito provavelmente estaríamos utilizando a variável pizza em outros métodos, logo trabalhar com ela sendo local seria uma escolha não eficiente.

Ok, mas ainda tenho outra dúvida, na verdade vi o Simple Factory outras vezes e não como um padrão, somente como um tipo alternativo de script. Ele é ou não um padrão?

Ops! Conheço bem essa história. Eu também o vi como um "não padrão”, alias logo quando o conheci. Porém consumindo mais literaturas sobre padrões adotei ele como sendo sim um padrão, até por que resolve ao menos um problema, mantendo os princípios, possíveis de serem seguidos, em linha e tem um modelo de como deve ser construído, ou seja, tudo que um padrão tem.

Antes de prosseguir com os pontos positivos e negativos do padrão proposto nesse artigo, note que a utilização de nossa classe Factory com o método de criação como static tirou de vez a possibilidade do segundo problema citado no início do artigo de ocorrer.

Qual problema?

O que o código cliente criava uma pizza específica (no caso era a de calabresa, PizzaCalabresa) direto no código cliente, somente para não instanciar a classe Pizzaria que era responsável por criar objetos Pizza.

Com o novo código em static não há necessidade de instâncias extras.

Dica para estudo:

Sabe os condicionais em PizzaFactory? Mais precisamente o parâmetro de entrada tipo. Se no código cliente for digitado errado o nome de uma pizza o que aconteceria?

Dê uma lida no método de refatoração de código: Substituir Código de Tipo Por Classe. Depois de ler você vai entender o problema que é utilizar esse tipo de algoritmo, digo, com comparação entre Strings ao invés de classes. Fique tranquilo que o método de refatoração indicado aqui não precisa de conhecimento prévio de algum padrão de projeto, alias ele é bem simples.

Ponto negativo

  • Caso as instâncias que são criadas por um Factory sejam necessárias poucas vezes no projeto, digo, o suficiente para deixar a implementação via Factory mais complicada. E assumindo também que essas instâncias são necessárias em classes de mesmo domínio do problema. Nesse caso o Factory pode acabar por atrapalhar ao invés de ajudar.

Pontos positivos

  • Aplicação de padrões e projetos de software aplicam nesses projetos o que chamamos de “linguagem universal”, pois outros programadores, tanto via código ou via documentação, somente de enxergarem a utilização do padrão já saberão como interpretar o código, colocando mais eficiência na leitura do projeto;
  • Remove o acoplamento forte existente entre classes que utilizavam diretamente a instanciação de classes;
  • Encapsula um código que pode variar a qualquer atualização de projeto (código de instanciação), criando assim um único ponto de alteração.

Conclusão

Visto que códigos clientes ou até mesmo códigos de classes do mesmo projeto, porém não muito relacionados. Visto que esses criam instâncias diretas de outras classes e com frequência são as mesmas classes sendo criadas, nesse contexto muito provavelmente um Simple Factory pode ser uma excelente opção como técnica de código limpo para obter maior performance de seu software.

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: Extrair ParâmetroRefatoração de Código: Extrair ParâmetroAndroid
Refatoração de Código: Substituir Código de Tipo Por ClasseRefatoração de Código: Substituir Código de Tipo Por ClasseAndroid
Padrão de Projeto: ObserverPadrão de Projeto: ObserverAndroid
Refatoração de Código: Substituir Notificações Hard-Coded Por ObserverRefatoração de Código: Substituir Notificações Hard-Coded Por ObserverAndroid

Compartilhar

Comentários Facebook

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...