Oi amigo! Hoje continuaremos estudando padrões de design com você. Nesta palestra falaremos sobre a Fábrica. Discutiremos com você qual problema é resolvido usando este modelo e veremos um exemplo de como uma fábrica ajuda a abrir uma cafeteria. E também darei 5 passos simples para criar uma fábrica. Para estar na mesma página com todos e compreender facilmente a essência, você deve estar familiarizado com os seguintes tópicos:
- Herança em Java
- Estreitando e expandindo tipos de referência em Java
- Interação entre diferentes classes e objetos
O que é uma fábrica?
O padrão de design Factory permite controlar a criação de objetos. O processo de criação de um novo objeto não é tão simples, mas também não é muito complicado. Todos sabemos que para criar um novo objeto devemos usar o arquivonew
. E pode parecer que não há nada para administrar aqui, mas não é assim. Podem surgir dificuldades quando nossa aplicação possui uma determinada classe que possui muitos descendentes, sendo necessário criar uma instância de uma determinada classe dependendo de algumas condições. Factory é um padrão de design que ajuda a resolver o problema de criação de objetos diferentes dependendo de algumas condições. Abstrato, não é? Mais especificidade e clareza aparecerão quando observarmos o exemplo abaixo.
Criamos diferentes tipos de café
Digamos que queremos automatizar uma cafeteria. Precisamos aprender a preparar diferentes tipos de café. Para isso, em nosso aplicativo criaremos uma classe de café e seus derivados: Americano, cappuccino, espresso, latte - esses tipos de café que iremos preparar. Vamos começar com a aula geral de café:public class Coffee {
public void grindCoffee(){
// перемалываем кофе
}
public void makeCoffee(){
// делаем кофе
}
public void pourIntoCup(){
// наливаем в чашку
}
}
A seguir, vamos criar seus herdeiros:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Nossos clientes irão pedir algum tipo de café, e essa informação precisa ser repassada ao programa. Isso pode ser feito de diferentes maneiras, por exemplo, usando String
. Mas é mais adequado para esses fins enum
. Vamos criar enum
e definir nele os tipos de café para os quais aceitamos pedidos:
public enum CoffeeType {
ESPRESSO,
AMERICANO,
CAFFE_LATTE,
CAPPUCCINO
}
Ótimo, agora vamos escrever o código da nossa cafeteria:
public class CoffeeShop {
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new Americano();
break;
case ESPRESSO:
coffee = new Espresso();
break;
case CAPPUCCINO:
coffee = new Cappucсino();
break;
case CAFFE_LATTE:
coffee = new CaffeLatte();
break;
}
coffee.grindCoffee();
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
return coffee;
}
}
O método orderCoffee
pode ser dividido em dois componentes:
- Criando uma instância específica de café em um bloco
switch-case
. É aqui que o que a Fábrica faz é a criação de um tipo específico dependendo das condições. - A preparação em si é moer, cozinhar e despejar em uma xícara.
- O próprio algoritmo de preparação (moer, cozinhar e despejar em uma xícara) permanecerá inalterado (pelo menos assim esperamos).
- Mas a gama de café pode mudar. Talvez comecemos a fazer mocha.. Mocha.. Mokkachi... Deus o abençoe, um novo tipo de café.
switch-case
. Também é possível que na nossa cafetaria o método orderCoffee
não seja o único local onde criamos diferentes tipos de café. Portanto, alterações terão que ser feitas em diversos locais. Você provavelmente já entendeu o que quero dizer. Precisamos refatorar. Mova o bloco responsável pela criação do café para uma classe separada por dois motivos:
- Poderemos reaproveitar a lógica de criação do café em outros lugares.
- Se o intervalo mudar, não teremos que editar o código em todos os lugares onde a criação de café será usada. Bastará alterar o código em apenas um local.
Estamos serrando nossa primeira fábrica
Para isso, vamos criar uma nova classe que será responsável apenas por criar as instâncias necessárias das classes coffee:public class SimpleCoffeeFactory {
public Coffee createCoffee (CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new Americano();
break;
case ESPRESSO:
coffee = new Espresso();
break;
case CAPPUCCINO:
coffee = new Cappucino();
break;
case CAFFE_LATTE:
coffee = new CaffeLatte();
break;
}
return coffee;
}
}
Parabéns! Acabamos de implementar o padrão de design Factory em sua forma mais simples. Embora tudo pudesse ser ainda mais simples se o método se tornasse createCoffee
estático. Mas então perderíamos duas possibilidades:
- Herdar
SimpleCoffeeFactory
e substituir ocreateCoffee
. - Implemente a implementação de fábrica necessária em nossas aulas.
Introdução de uma fábrica em uma cafeteria
Vamos reescrever nossa aula de cafeteria usando uma fábrica:public class CoffeeShop {
private final SimpleCoffeeFactory coffeeFactory;
public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
this.coffeeFactory = coffeeFactory;
}
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = coffeeFactory.createCoffee(type);
coffee.grindCoffee();
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
return coffee;
}
}
Ótimo. Agora vamos tentar descrever de forma esquemática e concisa a estrutura do padrão de design Factory.
5 passos para abrir sua própria fábrica
Passo 1. No seu programa você tem uma classe com vários descendentes, como na figura abaixo: Passo 2. Você cria uma classeenum
na qual você define uma variável enum para cada classe descendente:
enum CatType {
LION,
TIGER,
BARSIK
}
Etapa 3. Você constrói sua fábrica. Você chama MyClassFactory
, o código está abaixo:
class CatFactory {}
Etapa 4. Você cria um método em sua fábrica createMyClass
que usa a variável - enum
MyClassType
. Código abaixo:
class CatFactory {
public Cat createCat(CatType type) {
}
}
Etapa 5. Você escreve um bloco no corpo do método switch-case
no qual itera todos os valores enum e cria uma instância da classe correspondente ao enum
valor:
class CatFactory {
public Cat createCat(CatType type) {
Cat cat = null;
switch (type) {
case LION:
cat = new Barsik();
break;
case TIGER:
cat = new Tiger();
break;
case BARSIK:
cat = new Lion();
break;
}
return cat;
}
}
Que nem um chefe.
GO TO FULL VERSION