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

Padrões de design: fábrica abstrata

Publicado no grupo Random-PT
Oi! Hoje continuaremos a estudar os padrões de projeto e discutiremos o padrão de fábrica abstrato . Padrões de design: fábrica abstrata - 1Aqui está o que abordaremos na lição:
  • Discutiremos o que é uma fábrica abstrata e qual problema esse padrão resolve
  • Criaremos o esqueleto de um aplicativo de plataforma cruzada para pedir café por meio de uma interface de usuário
  • Estudaremos instruções sobre como usar esse padrão, incluindo olhar para um diagrama e código
  • E como bônus, esta lição inclui um ovo de Páscoa oculto que o ajudará a aprender como usar o Java para determinar o nome do sistema operacional e, dependendo do resultado, executar uma ou outra ação.
Para entender completamente esse padrão, você precisa ser bem versado nos seguintes tópicos:
  • herança em Java
  • classes e métodos abstratos em Java

Que problemas uma fábrica abstrata resolve?

Uma fábrica abstrata, como todos os padrões de fábrica, nos ajuda a garantir que novos objetos sejam criados corretamente. Nós o usamos para gerenciar a "produção" de várias famílias de objetos interconectados. Várias famílias de objetos interconectados... O que isso significa? Não se preocupe: na prática, tudo é mais simples do que parece. Para começar, o que poderia ser uma família de objetos interconectados? Suponha que estamos desenvolvendo uma estratégia militar envolvendo vários tipos de unidades:
  • infantaria
  • cavalaria
  • arqueiros
Esses tipos de unidades estão interligados, porque servem no mesmo exército. Poderíamos dizer que as categorias listadas acima são uma família de objetos interligados. Nós entendemos isso. Mas o padrão de fábrica abstrato é usado para organizar a criação de várias famílias de objetos interconectados. Também não há nada complicado aqui. Vamos continuar com o exemplo da estratégia militar. De um modo geral, as unidades militares pertencem a várias partes em guerra diferentes. Dependendo do lado em que estão, as unidades militares podem variar significativamente na aparência. Os soldados de infantaria, cavaleiros e arqueiros do exército romano não são iguais aos soldados de infantaria, cavaleiros e arqueiros vikings. Na estratégia militar, soldados de diferentes exércitos são diferentes famílias de objetos interligados. Seria engraçado se um programador' O erro de s fez com que um soldado com um uniforme francês da era de Napoleão, mosquete em punho, fosse encontrado caminhando entre as fileiras da infantaria romana. O padrão de projeto de fábrica abstrato é necessário precisamente para resolver esse problema. Não, não o problema do constrangimento que pode advir da viagem no tempo, mas o problema de criar vários grupos de objetos interconectados. Uma fábrica abstrata fornece uma interface para criar todos os produtos disponíveis (uma família de objetos). Uma fábrica abstrata geralmente tem várias implementações. Cada um deles é responsável pela criação dos produtos de uma das famílias. Nossa estratégia militar incluiria uma fábrica abstrata que cria soldados de infantaria, arqueiros e cavaleiros abstratos, bem como implementações dessa fábrica. Por exemplo, uma fábrica que cria legionários romanos e uma fábrica que cria soldados cartagineses. A abstração é o princípio orientador mais importante desse padrão. Os clientes da fábrica trabalham com a fábrica e seus produtos apenas por meio de interfaces abstratas. Como resultado, você não precisa pensar sobre quais soldados estão sendo criados no momento. Em vez disso, você passa essa responsabilidade para alguma implementação concreta da fábrica abstrata.

Vamos continuar automatizando nossa cafeteria

na última lição, estudamos o padrão do método de fábrica. Nós o usamos para expandir nosso negócio de café e abrir vários novos locais. Hoje vamos continuar a modernizar o nosso negócio. Usando o padrão de fábrica abstrato, estabeleceremos as bases para um novo aplicativo de desktop para pedir café online. Ao escrever um aplicativo de desktop, devemos sempre pensar no suporte multiplataforma. Nosso aplicativo deve funcionar tanto no macOS quanto no Windows (spoiler: o suporte para Linux é deixado para você implementar como lição de casa). Como será nosso aplicativo? Bem simples: será um formulário composto por um campo de texto, um campo de seleção e um botão. Se você tem experiência em usar diferentes sistemas operacionais, certamente notou que os botões no Windows são renderizados de forma diferente do que em um Mac. Como tudo mais... Bem, vamos começar.
  • botões
  • campos de texto
  • campos de seleção
Isenção de responsabilidade: em cada interface, podemos definir métodos como onClick, onValueChangedou onInputChanged. Em outras palavras, poderíamos definir métodos que nos permitiriam lidar com vários eventos (pressionar um botão, digitar um texto, selecionar um valor em uma caixa de seleção). Tudo isso é deliberadamente omitido aqui para não sobrecarregar o exemplo e para torná-lo mais claro à medida que estudamos o padrão de fábrica. Vamos definir interfaces abstratas para nossos produtos:
public interface Button {}
public interface Select {}
public interface TextField {}
Para cada sistema operacional, devemos criar elementos de interface no estilo do sistema operacional. Vamos escrever código para Windows e MacOS. Vamos criar implementações para Windows:
public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
Agora fazemos o mesmo para MacOS:
public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
Excelente. Agora podemos prosseguir para nossa fábrica abstrata, que criará todos os tipos de produtos abstratos disponíveis:
public interface GUIFactory {

    Button createButton();
    TextField createTextField();
    Select createSelect();

}
Soberbo. Como você pode ver, ainda não fizemos nada complicado. Tudo o que se segue também é simples. Por analogia com os produtos, criamos diversas implementações de fábrica para cada SO. Vamos começar com o Windows:
public class WindowsGUIFactory implements GUIFactory {
    public WindowsGUIFactory() {
        System.out.println("Creating GUIFactory for Windows OS");
    }

    public Button createButton() {
        System.out.println("Creating Button for Windows OS");
        return new WindowsButton();
    }

    public TextField createTextField() {
        System.out.println("Creating TextField for Windows OS");
        return new WindowsTextField();
    }

    public Select createSelect() {
        System.out.println("Creating Select for Windows OS");
        return new WindowsSelect();
    }
}
Adicionamos algumas saídas de console dentro dos métodos e do construtor para ilustrar melhor o que está acontecendo. Agora para macOS:
public class MacGUIFactory implements GUIFactory {
    public MacGUIFactory() {
        System.out.println("Creating GUIFactory for macOS");
    }

    @Override
    public Button createButton() {
        System.out.println("Creating Button for macOS");
        return new MacButton();
    }

    @Override
    public TextField createTextField() {
        System.out.println("Creating TextField for macOS");
        return new MacTextField();
    }

    @Override
    public Select createSelect() {
        System.out.println("Creating Select for macOS");
        return new MacSelect();
    }
}
Observe que cada assinatura de método indica que o método retorna um tipo abstrato. Mas dentro dos métodos, estamos criando implementações específicas dos produtos. Este é o único lugar onde controlamos a criação de instâncias específicas. Agora é hora de escrever uma classe para o formulário. Esta é uma classe Java cujos campos são elementos de interface:
public class CoffeeOrderForm {
    private final TextField customerNameTextField;
    private final Select coffeeTypeSelect;
    private final Button orderButton;

    public CoffeeOrderForm(GUIFactory factory) {
        System.out.println("Creating coffee order form");
        customerNameTextField = factory.createTextField();
        coffeeTypeSelect = factory.createSelect();
        orderButton = factory.createButton();
    }
}
Uma fábrica abstrata que cria elementos de interface é passada para o construtor do formulário. Passaremos a implementação de fábrica necessária ao construtor para criar elementos de interface para um determinado sistema operacional.
public class Application {
    private CoffeeOrderForm coffeeOrderForm;

    public void drawCoffeeOrderForm() {
        // Determine the name of the operating system through System.getProperty()
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // For Windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // For Mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS. Unable to draw form :(");
            return;
        }
        coffeeOrderForm = new CoffeeOrderForm(guiFactory);
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.drawCoffeeOrderForm();
    }
}
Se executarmos o aplicativo no Windows, obteremos a seguinte saída:
Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
Em um Mac, a saída será a seguinte:
Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
No Linux:
Unknown OS. Unable to draw form :(
E agora resumimos. Escrevemos o esqueleto de um aplicativo baseado em GUI no qual os elementos da interface são criados especificamente para o sistema operacional relevante. Vamos repetir concisamente o que criamos:
  • Uma família de produtos que consiste em um campo de entrada, um campo de seleção e um botão.
  • Diferentes implementações da família de produtos para Windows e macOS.
  • Uma fábrica abstrata que define uma interface para a criação de nossos produtos.
  • Duas implementações de nossa fábrica, cada uma responsável por criar uma família específica de produtos.
  • Um formulário (uma classe Java) cujos campos são elementos de interface abstratos que são inicializados com os valores necessários no construtor usando uma fábrica abstrata.
  • Classe Application Dentro desta classe, criamos um formulário, passando a implementação de fábrica desejada para seu construtor.
O resultado é que implementamos o padrão de fábrica abstrato.

Fábrica abstrata: como usar

Uma fábrica abstrata é um padrão de projeto para gerenciar a criação de várias famílias de produtos sem estar vinculado a classes de produtos concretos. Ao usar esse padrão, você deve:
  1. Definir famílias de produtos. Suponha que temos dois deles:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. Para cada produto da família, defina uma classe abstrata (interface). No nosso caso, temos:
    • ProductA
    • ProductB
  3. Dentro de cada família de produtos, cada produto deve implementar a interface definida na etapa 2.
  4. Crie uma fábrica abstrata, com métodos para criar cada produto definido na etapa 2. No nosso caso, esses métodos serão:
    • ProductA createProductA();
    • ProductB createProductB();
  5. Crie implementações de fábrica abstratas para que cada implementação controle a criação de produtos de uma única família. Para fazer isso, dentro de cada implementação da fábrica abstrata, você precisa implementar todos os métodos de criação para que eles criem e retornem implementações de produtos específicos.
O diagrama UML a seguir ilustra as instruções descritas acima: Padrões de design: fábrica abstrata - 3Agora escreveremos o código de acordo com estas instruções:
// Define common product interfaces
public interface ProductA {}
public interface ProductB {}

// Create various implementations (families) of our products
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}

public class SpecificProductA2 implements ProductA {}
public class SpecificProductB2 implements ProductB {}

// Create an abstract factory
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// Implement the abstract factory in order to create products in family 1
public class SpecificFactory1 implements AbstractFactory {

    @Override
    public ProductA createProductA() {
        return new SpecificProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new SpecificProductB1();
    }
}

// Implement the abstract factory in order to create products in family 2
public class SpecificFactory2 implements AbstractFactory {

    @Override
    public ProductA createProductA() {
        return new SpecificProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new SpecificProductB2();
    }
}

Trabalho de casa

Para reforçar o material, você pode fazer 2 coisas:
  1. Refine o aplicativo de pedido de café para que também funcione no Linux.
  2. Crie sua própria fábrica abstrata para produzir unidades envolvidas em qualquer estratégia militar. Isso pode ser uma estratégia militar histórica envolvendo exércitos reais ou uma fantasia com orcs, gnomos e elfos. O importante é escolher algo que te interesse. Seja criativo, imprima mensagens no console e divirta-se aprendendo sobre padrões!
Comentários
  • Populares
  • Novas
  • Antigas
Você precisa acessar para deixar um comentário
Esta página ainda não tem nenhum comentário