CodeGym/Blogue Java/Random-PT/Padrões de design: método de fábrica
John Squirrels
Nível 41
San Francisco

Padrões de design: método de fábrica

Publicado no grupo Random-PT
Oi! Hoje continuaremos a estudar os padrões de projeto e discutiremos o padrão de método de fábrica. Padrões de projeto: Método de fábrica - 1 Você descobrirá o que é e para quais tarefas esse padrão é adequado. Consideraremos esse padrão de projeto na prática e estudaremos sua estrutura. Para garantir que tudo esteja claro, você precisa entender os seguintes tópicos:
  1. Herança em Java.
  2. Métodos abstratos e classes em Java

Que problema o método de fábrica resolve?

Todos os padrões de projeto de fábrica têm dois tipos de participantes: criadores (as próprias fábricas) e produtos (os objetos criados pelas fábricas). Imagine a seguinte situação: temos uma fábrica que produz carros da marca CodeGym. Sabe criar modelos de carros com vários tipos de carrocerias:
  • sedãs
  • peruas
  • cupês
Nosso negócio prosperou tanto que um belo dia adquirimos outra montadora - a OneAuto. Sendo empresários sensatos, não queremos perder nenhum cliente OneAuto, e por isso nos deparamos com a tarefa de reestruturar a produção para que possamos produzir:
  • Sedãs CodeGym
  • Carrinhas CodeGym
  • CodeGym cupês
  • sedãs OneAuto
  • Carrinhas OneAuto
  • OneAuto cupê
Como você pode ver, em vez de um grupo de produtos, agora temos dois, e eles diferem em alguns detalhes. O padrão de design do método de fábrica é para quando precisamos criar diferentes grupos de produtos, cada um com algumas características específicas. Consideraremos o princípio orientador desse padrão na prática, passando gradualmente do simples ao complexo, usando o exemplo de nossa cafeteria, que criamos em uma das lições anteriores .

Um pouco sobre o padrão de fábrica

Deixe-me lembrá-lo de que construímos anteriormente uma pequena cafeteria virtual. Com a ajuda de uma fábrica simples, aprendemos a fazer diferentes tipos de café. Hoje vamos retrabalhar este exemplo. Vamos relembrar como era nossa cafeteria, com sua fábrica simples. Tivemos uma aula de café:
public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
E várias classes infantis correspondentes a tipos específicos de café que nossa fábrica poderia produzir:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Criamos um enum para facilitar a realização de pedidos:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
A própria fábrica de café era assim:
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 Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }

        return coffee;
    }
}
E, finalmente, a própria cafeteria ficou assim:
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("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}

Modernizando uma fábrica simples

Nosso café está funcionando muito bem. Tanto que estamos pensando em expandir. Queremos abrir alguns novos locais. Somos ousados ​​e empreendedores, então não vamos criar cafeterias chatas. Queremos que cada loja tenha um toque especial. Assim, para começar, abriremos duas lojas: uma italiana e outra americana. Essas mudanças afetarão não apenas o design de interiores, mas também as bebidas oferecidas:
  • na cafeteria italiana, utilizaremos exclusivamente marcas de café italiano, com moagem e torrefação especiais.
  • o local americano terá porções maiores e serviremos marshmallows com cada pedido.
A única coisa que permanece inalterada é o nosso modelo de negócio, que tem se mostrado excelente. Em termos de código, é isso que acontece. Tínhamos 4 classes correspondentes aos nossos produtos:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Mas agora teremos 8:
public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
Como queremos manter o modelo de negócios atual, queremos que o orderCoffee(CoffeeType type)método sofra o mínimo possível de alterações. Dê uma olhada nisto:
public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Here's your coffee! Thanks! Come again!");
    return coffee;
}
Que opções temos? Bem, já sabemos como escrever uma fábrica, certo? A coisa mais simples que vem imediatamente à mente é escrever duas fábricas semelhantes e, em seguida, passar a implementação desejada para o construtor de nossa cafeteria. Ao fazer isso, a classe da cafeteria não mudará. Primeiro, precisamos criar uma nova classe de fábrica, fazer com que ela herde nossa fábrica simples e, em seguida, sobrescrever o createCoffee(CoffeeType type)método. Vamos escrever fábricas para criar café ao estilo italiano e café ao estilo americano:
public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
Agora podemos passar a implementação de fábrica desejada para a CoffeeShop. Vamos ver como seria o código para pedir café em diferentes cafeterias. Por exemplo, cappuccino estilo italiano e estilo americano:
public class Main {
    public static void main(String[] args) {
        /*
            Order an Italian-style cappuccino:
            1. Create a factory for making Italian coffee
            2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Order an American-style cappuccino
            1. Create a factory for making American coffee
            2. Create a new coffee shop, passing the American coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Criamos duas cafeterias diferentes, passando a fábrica desejada para cada uma. Por um lado, atingimos nosso objetivo, mas, por outro lado... De alguma forma, isso não agrada aos empresários... Vamos descobrir o que está errado. Primeiro, a abundância de fábricas. O que? Agora, para cada novo local, devemos criar sua própria fábrica e, além disso, garantir que a fábrica relevante seja passada para o construtor ao criar uma cafeteria? Em segundo lugar, ainda é uma fábrica simples. Apenas modernizado ligeiramente. Mas estamos aqui para aprender um novo padrão. Em terceiro lugar, não é possível uma abordagem diferente? Seria ótimo se pudéssemos colocar todas as questões relacionadas ao preparo do café noCoffeeShopclasse, vinculando os processos de criação de café e pedidos de atendimento, mantendo simultaneamente flexibilidade suficiente para fazer vários estilos de café. A resposta é sim, podemos. Isso é chamado de padrão de design do método de fábrica.

De uma fábrica simples para um método de fábrica

Para resolver a tarefa da forma mais eficiente possível:
  1. Retornamos o createCoffee(CoffeeType type)método para a CoffeeShopclasse.
  2. Faremos esse método abstrato.
  3. A CoffeeShopprópria classe se tornará abstrata.
  4. A CoffeeShopturma terá turmas filhas.
Sim amigo. A cafeteria italiana nada mais é do que uma descendente da CoffeeShopclasse, que implementa o createCoffee(CoffeeType type)método de acordo com as melhores tradições dos baristas italianos. Agora, um passo de cada vez. Passo 1. Torne a Coffeeclasse abstrata. Temos duas famílias inteiras de produtos diferentes. Ainda assim, os cafés italiano e americano têm um ancestral comum – a Coffeeclasse. Seria apropriado torná-lo abstrato:
public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Passo 2. Faça CoffeeShopabstrato, com um createCoffee(CoffeeType type)método abstrato
public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Etapa 3. Crie uma cafeteria italiana, descendente da cafeteria abstrata. Implementamos o createCoffee(CoffeeType type)método nele, levando em consideração as especificidades das receitas italianas.
public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
Etapa 4. Fazemos o mesmo para a cafeteria estilo americano
public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
Passo 5. Confira como ficarão os lattes americanos e italianos:
public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
Parabéns. Acabamos de implementar o padrão de design do método de fábrica usando nossa cafeteria como exemplo.

O princípio por trás dos métodos de fábrica

Agora vamos considerar com mais detalhes o que obtivemos. O diagrama abaixo mostra as classes resultantes. Os blocos verdes são classes de criadores e os blocos azuis são classes de produtos. Padrões de projeto: Método de fábrica - 2Que conclusões podemos tirar?
  1. Todos os produtos são implementações da Coffeeclasse abstrata.
  2. Todos os criadores são implementações da CoffeeShopclasse abstrata.
  3. Vemos duas hierarquias de classes paralelas:
    • Hierarquia de produtos. Vemos descendentes de italianos e descendentes de americanos
    • Hierarquia dos criadores. Vemos descendentes de italianos e descendentes de americanos
  4. A CoffeeShopsuperclasse não possui informações sobre qual produto específico ( Coffee) será criado.
  5. A CoffeeShopsuperclasse delega a criação de um produto específico para seus descendentes.
  6. Cada descendente da CoffeeShopclasse implementa um createCoffee()método fábrica de acordo com suas especificidades. Em outras palavras, as implementações das classes produtoras preparam produtos específicos com base nas especificidades da classe produtora.
Agora você está pronto para a definição do padrão de método de fábrica . O padrão Factory Method define uma interface para criar um objeto, mas permite que as subclasses selecionem a classe do objeto criado. Assim, um método fábrica delega a criação de uma instância para subclasses. Em geral, lembrar a definição não é tão importante quanto entender como tudo funciona.

Estrutura de um método fábrica

Padrões de projeto: Método de fábrica - 3O diagrama acima mostra a estrutura geral do padrão de método de fábrica. O que mais é importante aqui?
  1. A classe Creator implementa todos os métodos que interagem com produtos, exceto o método fábrica.
  2. O método abstrato factoryMethod()deve ser implementado por todos os descendentes da Creatorclasse.
  3. A ConcreteCreatorclasse implementa o factoryMethod()método, que cria diretamente o produto.
  4. Esta classe é responsável por criar produtos específicos. Esta é a única aula com informações sobre como criar esses produtos.
  5. Todos os produtos devem implementar uma interface comum, ou seja, devem ser descendentes de uma classe de produto comum. Isso é necessário para que classes que usam produtos possam operar neles como abstrações, ao invés de implementações específicas.

Trabalho de casa

Hoje, trabalhamos bastante e estudamos o padrão de projeto do método de fábrica. É hora de reforçar o material! Exercício 1. Faça o trabalho de abrir outra cafeteria. Pode ser uma cafeteria no estilo inglês ou no estilo espanhol. Ou até mesmo no estilo de nave espacial. Adicione corante alimentício ao café para fazê-lo brilhar, e seu café ficará simplesmente fora deste mundo! Exercício 2. Na última lição , você fez um exercício em que criou um sushi bar virtual ou uma pizzaria virtual. Agora seu exercício é não ficar parado. Hoje você aprendeu como usar o padrão de método de fábrica a seu favor. É hora de usar esse conhecimento e expandir seu próprio negócio ;)
Comentários
  • Populares
  • Novas
  • Antigas
Você precisa acessar para deixar um comentário
Esta página ainda não tem nenhum comentário