Extrair a lógica de criação de objetos para classes e métodos específicos é o objetivo dos padrões Factory.
Entre os Padrões de Projeto GoF encontramos os padrões Factory Method e Abstract Factory. Entretanto, existe uma variação mais simples do padrão Abstract Factory, ao qual chamamos de Simple Factory e é apresentado nesse artigo.
O problema que padrões fábrica resolvem é criar instâncias de objetos separadas do resto da lógica; por isso são classificados como padrões de criação.
Contudo, Simple Factory, Factory Method e Abstract Factory diferem bastante na solução para o problema, o que torna a escolha de qual usar muito dependente do contexto em que serão usados.
Uma nomenclatura comum ao falar sobre padrões Factory é chamar os objetos criados de produtos, e as classes que os criam são as fábricas — utilizando a metáfora de que fábricas fazem produtos.
O contexto de utilização do Simple Factory é quando você possui apenas um tipo produto para ser criado e existe apenas uma maneira de fazê-lo, ou seja, apenas uma fábrica.
Participantes de uma Simple Factory e suas relações
A solução é criar uma classe para extrair o comportamento de criação de objetos.
Essa solução utiliza duas técnicas básicas de refatoração chamados Extrair Classe e Mover Método para separar completamente responsabilidades em duas classes diferentes.
O Simple Factory atende o objetivo de encapsular a criação de objetos e fornecer uma interface comum para criar diferentes tipos de objetos.
Criação de uma família de produtos sem fábricas
Modelo de aplicação Simple Factory
O Simple Factory não é um padrão tão complicado, é apenas uma nova classe com alguns métodos especializados em criar outros objetos.
Na verdade, Simple Factory nem é considerado um padrão por alguns autores (por não ser um padrão GoF), mas sim um "idioma de linguagem" ou pré-padrão.
Um "idioma de linguagem" (language idiom) refere-se a uma maneira comum de escrever código em uma linguagem específica para atingir um objetivo específico.
Apesar de simples, Simple Factory resolve o problema sem criar uma estrutura de classes muito grande.
Porém, caso a quantidade de código na classe fábrica comece a aumentar demais, talvez seja um indicador de que é melhor criar novas fábricas, ou até mesmo avaliar a utilização de outro padrão.
class Main { // Aqui a classe Main representa a classe Cliente
public static void main(String[] args) {
ProductSimpleFactory factory = new ProductSimpleFactory(); // Cria uma instância da fábrica
// Pede para a fábrica criar uma instância de produto
Product p01 = factory.createProduct("A");
// Usando o produto criado normalmente...
System.out.println(p01.getName());
p01.operation();
Product p02 = factory.createProduct("B");
System.out.println(p02.getName());
p02.operation();
}
}
public class ProductSimpleFactory {
public Product createProduct(String type) {
Product product = null;
if (type.equals("A")) {
product = new ConcreteProductA();
} else if (type.equals("B")) {
product = new ConcreteProductB();
} else {
System.out.println("Tente novamente.");
}
return product;
}
}
public abstract class Product {
String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void operation() {
System.out.println("Executando método da instância de " + name);
}
}
public class ConcreteProductA extends Product {
public ConcreteProductA() {
name = "Produto A";
}
}
public class ConcreteProductB extends Product {
public ConcreteProductB() {
name = "Produto B";
}
}
class Main {
public static void main(String[] args) {
AparelhoMedico monitor = AparelhoMedicoFabrica.criarAparelhoMedico("monitorCardiaco");
AparelhoMedico termometro = AparelhoMedicoFabrica.criarAparelhoMedico("termometro");
// Utilizando os dispositivos médicos
monitor.monitorarParametro();
termometro.monitorarParametro();
}
}
public class AparelhoMedicoFabrica {
public static AparelhoMedico criarAparelhoMedico(String tipo) {
if (tipo.equalsIgnoreCase("monitorCardiaco")) {
return new MonitorCardiaco();
} else if (tipo.equalsIgnoreCase("termometro")) {
return new Termometro();
} else {
throw new IllegalArgumentException("Tipo de dispositivo médico inválido.");
}
}
}
interface AparelhoMedico {
void monitorarParametro();
}
public class MonitorCardiaco implements AparelhoMedico {
@Override
public void monitorarParametro() {
System.out.println("Monitorando o ritmo cardíaco do paciente.");
}
}
public class Termometro implements AparelhoMedico {
@Override
public void monitorarParametro() {
System.out.println("Medindo a temperatura do paciente.");
}
}
Existe um design semelhante em que uma fábrica é definida como um método estático. Qual é a diferença?
Uma fábrica simples com um método estático é uma técnica comum e geralmente é chamada de fábrica estática.
Com um método estático não é necessário instanciar um objeto para usar o método de criação. Mas, tem a desvantagem de não poder criar subclasses e alterar o comportamento do método de criação.