Padrão de Projeto: Factory Method

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes! Você receberá um email de confirmação. Somente depois de confirma-lo é que eu poderei lhe enviar os conteúdos semanais exclusivos. Os artigos em PDF são entregues somente para os inscritos na lista.

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

Padrão de Projeto: Factory Method

Vinícius Thiengo
(8007) (4)
Go-ahead
"Você não começa com tudo a construir uma parede. Você não diz 'eu estou indo para construir a maior parede que já foi construída.' Você não começa por aí. Você diz: 'Eu vou colocar este tijolo tão perfeitamente como um tijolo pode ser colocado.' Você faz isso todos os dias. E logo você tem uma muralha."
Will Smith
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
Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba gratuitamente conteúdos Android sem precedentes!
Email inválido

Opa, blz?

Nesse artigo vamos abordar o padrão de projeto Factory Method. Note que esse é o segundo Factory recomendado na linha de estudos dos padrões Factory. O primeiro é o Simple Factory e o terceiro o Abstract Factory.

Para ter um conteúdo ainda mais completo sobre os padrões Factory, recomendo que leia primeiro o artigo sobre o padrão Simple Factory.

Tópicos presentes no artigo:

Apresentação

O Factory Method tem em semelhança ao padrão Simple Factory o nome e o objetivo de isolar código de criação (aquele que utiliza o new) das classes de negócio do projeto.

O que você quer dizer com o termo "classes de negócio"?

Essas são as classes específicas de seu projeto, as que têm as lógicas para que o software que você criou resolva o problema que ele foi proposto a resolver. Classes de negócio também são conhecidas como classes de domínio do problema. 

Qual a real diferença do Factory Method para as outras versões de Factory?

O Factory Method delega a implementação do código de criação para as suas subclasses ou classes que o implementam.

Implementam? Você diz Interface?

Sim. Também podemos trabalhar com o Factory Method por meio de uma Interface. Porém, quando implementado via Interface, em algumas linguagens nós vamos perder o poder de além de ter a assinatura de como deve ser o método de criação, ter também a definição de alguns métodos comuns as classes que implementam o Factory Method.

Diagrama

Não tão simples quanto o diagrama do Simple Factory, porém ainda fácil de entender:

ConcreteFactory? O que quer dizer com isso?

As subclasses de uma Factory abstrata, digo, as subclasses concretas, são também conhecidas como fábricas concretas ou concrete factories.

Os códigos para gerarem as instâncias de classes do domínio do problema estão alocados   nos métodos fábrica das classes ConcreteFactory_1 e ConcreteFactory_2.

Código de exemplo

O código de exemplo é um trecho de um projeto de software para Pizzaria, similar ao utilizado no artigo do Simple Factory. Vamos começar com o código da classe Pizzaria:

public class Pizzaria {
private Pizza pizza;

public void criarPizza( String cidade, String tipo ){

if( cidade.equals("sao-paulo") ){

if( tipo.equals("queijo") ){
pizza = new SPPizzaQuatroQueijos();
}
else if( tipo.equals("portuguesa") ){
pizza = new SPPizzaPortuguesa();
}
else if( tipo.equals("calabresa") ){
pizza = new SPPizzaCalabresa();
}
}
else if( cidade.equals("rio-de-janeiro") ){

if( tipo.equals("queijo") ){
pizza = new RJPizzaQuatroQueijos();
}
else if( tipo.equals("portuguesa") ){
pizza = new RJPizzaPortuguesa();
}
else if( tipo.equals("calabresa") ){
pizza = new RJPizzaCalabresa();
}
}
}

public Pizza delivery(){
return pizza;
}
...
}

 

No código acima, com a criarão de instância dentro de uma classe de domínio de problema, onde há lógica de negocio além dos códigos de criação, nessa classe, devido a esse código de criação, temos então um projeto fortemente acoplado, algo ruim, pois diminui a eficiência de evolução do software.

Um exemplo desse problema de código fortemente acoplado acontece quando qualquer outro trecho de código do projeto que precise de uma instância específica de Pizza tenha de recorrer a uma instância de Pizzaria. Apesar de estarem Pizza e Pizzaria no mesmo domínio do problema, essas têm objetivos diferentes.

No código acima, como você deve ter notado, o real objetivo é permitir criar pizzas ou da cidade de São Paulo e da cidade de Rio de Janeiro. As pizzas nesse caso têm ingredientes e processos de produção distintos.

E aquele método delivery(), o que ele está fazendo ali?

Ele vai ajudar a entender um outro potencial do Factory Method, o de evitar repetição de código comum em métodos com lógica de negócio.

Voltando ao código de Pizzaria, note que temos de ter ao menos duas novas classes, uma para a cidade de São Paulo e outra para a cidade de Rio de Janeiro. Essas duas novas classes serão as classes Concrete Factory que herdaram de nossa classe Factory Method. Os nomes serão: PizzaFactorySaoPaulo e PizzaFactoryRioDeJaneiro. A classe referente ao Factory Method será a PizzaFactory.

Começando pelo código da classe abstrata PizzaFactory:

public abstract class PizzaFactory {
protected Pizza pizza;

public abstract void criarPizza( String tipo );

public Pizza delivery(){
return pizza;
}
}

 

Veja onde já colocamos o método delivery(), tudo indica que não mais teremos a classe Pizzaria. Na verdade até poderíamos ter aproveitado o nome, não teria problemas, somente optei pelo PizzaFactory para ficar de acordo com as classes herdam dela. Segue código de PizzaFactorySaoPaulo:

public class PizzaFactorySaoPaulo extends PizzaFactory {

@Override
public void criarPizza(String tipo) {

if( tipo.equals("queijo") ){
pizza = new SPPizzaQuatroQueijos();
}
else if( tipo.equals("portuguesa") ){
pizza = new SPPizzaPortuguesa();
}
else if( tipo.equals("calabresa") ){
pizza = new SPPizzaCalabresa();
}
}
}

 

Agora a implementação de PizzaFactoryRioDeJaneiro:

public class PizzaFactoryRioDeJaneiro extends PizzaFactory {

@Override
public void criarPizza(String tipo) {

if( tipo.equals("queijo") ){
pizza = new RJPizzaQuatroQueijos();
}
else if( tipo.equals("portuguesa") ){
pizza = new RJPizzaPortuguesa();
}
else if( tipo.equals("calabresa") ){
pizza = new RJPizzaCalabresa();
}
}
}

 

Com isso temos nossa Factory Method implementada. A implementação do método de criação foi delegada com sucesso para as fábricas concretas.

Os métodos comuns que estavam dentro da extinta classe Pizzaria poderão ser acessados pelas instâncias de PizzaFactorySaoPaulo e PizzaFactoryRioDeJaneiro, pois esses métodos comuns estão agora em PizzaFactory. Em nosso caso colocamos como exemplo para entendimento somente o método delivery().

Lembra da dúvida sobre o método delivery()? Está ai o porquê. Caso não colocássemos ele na classe factory, cada concrete factory class deveria implementá-lo, nesse caso: sinônimo de código duplicado.

Vamos a um código cliente do projeto de pizzaria:

public class TestPizzarias {

public static void main( String[] args ){
/* SÃO PAULO */
PizzaFactory pizzariaSaoPaulo = new PizzaFactorySaoPaulo();
pizzariaSaoPaulo.criarPizza("queijo");
Pizza pizza = pizzariaSaoPaulo.delivery();

/* RIO DE JANEIRO */
PizzaFactory pizzariaRioDeJaneiro = new PizzaFactoryRioDeJaneiro();
pizzariaRioDeJaneiro.criarPizza("calabresa");
pizza = pizzariaRioDeJaneiro.delivery();
}
}

 

Depois de todo esse código você deve estar se perguntando o por quê de manter os nomes das cidades, certo?

Tendo em mente que os ingredientes e modos de preparo das pizzas são distintos nas duas cidades, as classes de pizzas também precisam ser específicas de cada cidade, isso sabendo que o propósito aqui é apresentar o Factory Method, logo, com esse padrão, essa é a melhor opção de código.

Como assim "ingredientes e modos de preparo distintos"?

Caso em São Paulo a criação de uma pizza de calabresa for:

  1. primeiro massa;
  2. depois queijo;
  3. depois salsa;
  4. depois calabresa.

E no Rio de Janeiro for:

  1. primeiro massa;
  2. depois salsa;
  3. depois pimenta;
  4. depois queijo;
  5. depois calabresa.

Com essas diferenças para o mesmo tipo de pizza temos baixo nível de reaproveitamento dos códigos de Pizza. Internamente o algoritmo tem ao menos ordens e atributos distintos.

Dica de estudo

Caso não tenha ainda lido o artigo sobre o padrão Simple Factory, abaixo uma dica de estudo baseado em um problema nas implementações que utilizam comparação de Strings com valores estáticos.

O conteúdo que explica o problema e propõe uma solução está no artigo do método de refatoração Substituir Código de Tipo Por Classe.

Ponto negativo

  • Se a criação de subclasses, responsáveis pela instanciação das classes de domínio do problema, for apenas para cobrir criações diretas de poucas classes que são utilizadas poucas vezes e dentro de classes de mesmo domínio do problema, nesse contexto pode ser que o Factory Method atrapalhe ao invés de ser uma melhor opção quando comparado a versão de instanciação direta utilizando o palavra reservada new (caso raro o Factory ser um problema).

Pontos positivos

  • Por ser um padrão de software, a implementação do factory Method implica na também implementação, mesmo que de forma implícita, de uma linguagem universal no código, ou seja, outros developers que conhecem o padrão já entenderão o código e sem precisar ir aos detalhes;
  • Como com todas as versões de Factory, remove o acoplamento forte que existia nas classes que tinham, além da lógica de negócio própria delas, os códigos de criação de instâncias;
  • Encapsula o código que varia, código de instanciação de classe. Cada código de criação fica em um único local, em sua própria classe, criando assim um ponto único de atualização. Exemplo? Caso tenha de alterar o construtor da classe, digo, como ele é utilizado pelas classes clientes, deverá alterar apenas esse algoritmo no método de criação e não em todos os códigos clientes que utilizariam a criação direta.

Conclusão

O Factory Method permiti ter mais do que apenas o método de criação (assinatura desse método) na estrutra de classe fábrica e isso pode parecer uma vantagem, porém é somente um modo diferente de trabalhar com Factory, nem melhor nem pior que as outras duas formas, isso porque não existe um padrão melhor que o outro. Eles têm um melhor contexto (ou problema) para serem utilizados, com isso cada um pode ser a melhor escolha.

Se seu código de criação está em uma classe que pode se tornar um Factory, pois há (ou podem ser criadas) subclasses que se responsabilizem pelas criações das instâncias necessárias, nesse contexto, o Factory Method provavelmente é um excelente opção.

Caso a pizzaria tivesse um padrão na construção de pizzas, algo comum a todos os estados, mudando apenas a intensidade dos ingredientes, por exemplo: São Paulo tende a ter a massa mais grossa do que no Rio de Janeiro. Caso esse seja o cenário, o Abstract Factory seria a solução ideal.

Fontes

Use a Cabeça! Padrões de Projetos

Vlw.

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes!
Email inválido

Relacionado

Refatoração de Código: Mover Acumulação Para Parâmetro ColetorRefatoração de Código: Mover Acumulação Para Parâmetro ColetorAndroid
Refatoração de Código: Limitar Instanciação Com SingletonRefatoração de Código: Limitar Instanciação Com SingletonAndroid
Refatoração de Código: Unificar InterfacesRefatoração de Código: Unificar InterfacesAndroid
Padrão de Projeto: ObserverPadrão de Projeto: ObserverAndroid

Compartilhar

Comentários Facebook

Comentários Blog (4)

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...
22/03/2020
Seu blog tem me ajudado muito pra entender melhor design patterns mas só uma sugestão que eu queria dizer é que seria legal se colocasse o diagrama UML de todo o projeto já pronto, só pra melhor entendimento e visualização de como fica o projeto no final já com o padrão de projeto factory method.
Responder
Vinícius Thiengo (0) (0)
29/03/2020
Cris, tudo bem?

Obrigado pela dica.

Vou melhorar esse ponto em novos artigos sobre Engenharia de Software.

De qualquer forma: surgindo dúvidas, pode enviar.

Abraço.
Responder
Levi Saturnino (0) (0)
02/02/2020
A cada dia estou estudando fortemente Padrões de projetos.
E o que sentir falta no site e no livro, foi as construções da classe ao todo.
O tipo object Pizza que está em PizzaFactory ela é um classe ou um interface?
RJPizzaCalabresa ele tem uma extends ou implemente de pizza?

São essas dúvidas que me deixa pairando no ar????
Responder
Vinícius Thiengo (0) (0)
16/02/2020
Levi, tudo bem?

Você tem razão. Vou guardar essas dicas de melhoria para a próxima edição do livro.

Sobre o padrão Factory, na verdade, sobre todos os padrões Factory... tanto faz Pizza ser uma classe ou uma interface.

Esse tipo de regra de negócio em tipos que estão sendo utilizados dentro de classes Factory... essa regra de negócio não existe.

Sobre a dúvida se as classe de pizza específicas implementam ou herdam de Pizza. De acordo com o código, se for uma Interface, elas implementam Pizza, caso contrário elas herdam.

Mas na verdade a interpretação deve ser que: todos os tipos trabalhados internamente em classes Factory Method devem ser um subtipo da propriedade principal da classe Factory Method, aqui a propriedade "pizza".

Levi, é isso.

Surgindo mais dúvidas, pode perguntar.

Abraço.
Responder