Padrão de Projeto: Factory Method

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: Factory Method

Padrão de Projeto: Factory Method

Vinícius Thiengo
(4188)
Go-ahead
"Sempre procure algo que possa ser aprimorado. Nunca, jamais, conforme-se com o lugar onde está. A maneira como você atinge seus objetivos é sempre experimentar, até que consiga perceber se atingiu um ponto mais alto."
Jeff Sutherland
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 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.

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
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

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