
Padrão de Projeto: Factory Method
(17223) (5)

CategoriasAndroid, Design, Protótipo
AutorVinÃcius Thiengo
VÃdeo aulas186
Tempo15 horas
ExercÃciosSim
CertificadoSim

CategoriaEngenharia de Software
Autor(es)Eric Evans
EditoraAlta Books
Edição3ª
Ano2016
Páginas528
Tudo bem?
Neste artigo vamos abordar o padrão de projeto Factory Method (Método Fábrica).
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.
Antes de prosseguir, não esqueça de se inscrever na 📫 lista de e-mails do Blog para receber em primeira mão todos os conteúdos exclusivos sobre desenvolvimento e codificação limpa.
A seguir os tópicos abordados 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:
- primeiro massa;
- depois queijo;
- depois salsa;
- depois calabresa.
E no Rio de Janeiro for:
- primeiro massa;
- depois salsa;
- depois pimenta;
- depois queijo;
- 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.
Então é isso.
Por fim, não deixe de se inscrever na 📩 lista de e-mails do Blog para receber os conteúdos de desenvolvimento e codificação limpa exclusivos... e em primeira mão.
Abraço.
Fonte
Use a Cabeça! Padrões de Projetos
Relacionado
Comentários Blog (5)





Comentários Facebook