JavaRush /Blogue Java /Random-PT /Padrões de Design: AbstractFactory

Padrões de Design: AbstractFactory

Publicado no grupo Random-PT
Olá! Hoje continuaremos estudando padrões de projeto e falando sobre a fábrica abstrata . Padrões de Design: AbstractFactory - 1O que faremos durante a palestra:
  • Vamos discutir o que é uma fábrica abstrata e qual problema esse padrão resolve;
  • criaremos a estrutura de um aplicativo multiplataforma para pedido de café com interface de usuário;
  • Vamos estudar as instruções para usar esse padrão com diagrama e código;
  • Como bônus, há um ovo de Páscoa escondido na palestra, graças ao qual você aprenderá a determinar o nome do sistema operacional usando Java e, dependendo do resultado, realizar uma ou outra ação.
Para compreender totalmente esse padrão, você precisa ter um bom entendimento dos seguintes tópicos:
  • herança em Java;
  • classes e métodos abstratos em Java.

Que problemas o padrão abstrato de fábrica resolve?

A fábrica abstrata, como todos os padrões de fábrica, nos ajuda a organizar corretamente a criação de novos objetos. Com sua ajuda, gerenciamos a “liberação” de diversas famílias de objetos interligados. Várias famílias de objetos inter-relacionados...O que é isso? Não se preocupe: na prática tudo é mais simples do que parece. Vamos começar com o que poderia ser uma família de objetos relacionados? Suponha que você e eu estejamos desenvolvendo uma estratégia e que ela contenha várias unidades de combate:
  • infantaria;
  • cavalaria;
  • arqueiros.
Esses tipos de unidades de combate estão relacionados entre si, pois servem no mesmo exército. Podemos dizer que as categorias listadas acima constituem uma família de objetos inter-relacionados. Isso está resolvido. Mas o padrão abstrato de fábrica é usado para organizar a criação de várias famílias de objetos interconectados. Nada complicado aqui também. Vamos continuar o exemplo com estratégia. Eles geralmente têm vários lados opostos diferentes. As unidades de combate de diferentes lados podem diferir 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 dos vikings. No âmbito da estratégia, os soldados de diferentes exércitos são diferentes famílias de objetos interligados. Seria engraçado se, por erro de um programador, um soldado com uniforme francês da época de Napoleão, com um mosquete em punho, andasse entre a infantaria romana. É para resolver esse problema que o padrão abstrato de projeto de fábrica é necessário. Não, não os problemas do constrangimento da viagem no tempo, mas a criação de vários grupos de objetos interconectados. Uma fábrica abstrata fornece uma interface para criar todos os produtos existentes (objetos de família). Uma fábrica abstrata normalmente possui múltiplas implementações. Cada um deles é responsável pela criação de produtos de uma das variações. Como parte da estratégia, teríamos uma fábrica abstrata que cria infantaria, arqueiros e cavalaria abstratas, bem como implementações desta fábrica. Uma fábrica que cria legionários romanos e, por exemplo, uma fábrica que cria guerreiros cartagineses. A abstração é o princípio mais importante desse padrão. Os clientes da fábrica trabalham com ele e com os produtos apenas por meio de interfaces abstratas. Portanto, não precisamos pensar sobre que tipo de guerreiros estamos criando atualmente, mas sim transferir essa responsabilidade para alguma implementação específica da fábrica abstrata.

Continuamos a automatizar a cafeteria

Na última palestra estudamos o padrão do método fábrica, com o qual conseguimos expandir o negócio de café e abrir diversos novos pontos de venda de café. Hoje continuaremos nosso trabalho para modernizar nossos negócios. Usando o padrão abstrato de fábrica, estabeleceremos as bases para um novo aplicativo de desktop para pedidos de café online. Quando escrevemos um aplicativo para desktop, devemos sempre pensar em plataforma cruzada. Nosso aplicativo deve funcionar tanto no macOS quanto no Windows (spoiler: o Linux será deixado para você como lição de casa). Como será nosso aplicativo? Muito simples: será um formulário que consiste em um campo de texto, um campo de seleção e um botão. Se você tem experiência no uso de diferentes sistemas operacionais, certamente notou que no Windows os botões são renderizados de forma diferente do que no Mac. Como tudo mais... Então, vamos começar. No papel de famílias de produtos, como você provavelmente já entendeu, teremos elementos de interface gráfica:
  • botões;
  • campos de texto;
  • campos para seleção.
Isenção de responsabilidade. Dentro de cada interface poderíamos definir métodos como onClickou . Aqueles. métodos que nos permitirão lidar com vários eventos (clicar em um botão, inserir texto, selecionar um valor em uma caixa de seleção). Tudo isso é omitido deliberadamente para não sobrecarregar o exemplo e torná-lo mais visual para o estudo do padrão de fábrica. Vamos definir interfaces abstratas para nossos produtos: onValueChangedonInputChanged
public interface Button {}
public interface Select {}
public interface TextField {}
Para cada sistema operacional, devemos criar elementos de interface no estilo desse sistema operacional. Escrevemos 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 o mesmo para MacOS:
public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
Ótimo. Agora podemos iniciar nossa fábrica abstrata, que criará todos os tipos de produtos abstratos existentes:
public interface GUIFactory {

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

}
Perfeito. Como você pode ver, nada complicado até agora. Então tudo é igualmente simples. Por analogia com os produtos, criamos diferentes implementações de nossa fábrica para cada sistema operacional. Vamos começar com o Windows:
public class WindowsGUIFactory implements GUIFactory {
    public WindowsGUIFactory() {
        System.out.println("Creating gui factory 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();
    }
}
A saída do console dentro de métodos e construtores foi adicionada para demonstrar melhor como funciona. Agora para macOS:
public class MacGUIFactory implements GUIFactory {
    public MacGUIFactory() {
        System.out.println("Creating gui factory 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();
    }
}
Nota: cada método, de acordo com sua assinatura, retorna um tipo abstrato. Mas dentro do método criamos uma implementação concreta do produto. Este é o único lugar onde controlamos a criação de instâncias específicas. Agora é hora de escrever a classe do formulário. Esta é uma classe Java cujos campos são elementos de interface:
public class OrderCoffeeForm {
    private final TextField customerNameTextField;
    private final Select coffeTypeSelect;
    private final Button orderButton;

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

    public void drawOrderCoffeeForm() {
        // Определим Name операционной системы, получив meaning системной проперти через System.getProperty
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // Для windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // Для mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS, can't draw form :( ");
            return;
        }
        orderCoffeeForm = new OrderCoffeeForm(guiFactory);
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.drawOrderCoffeeForm();
    }
}
Se executarmos o aplicativo no Windows, obteremos o seguinte resultado:

Creating gui factory for Windows OS
Creating order coffee 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 gui factory for macOS
Creating order coffee form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
No Linux:

Unknown OS, can't draw form :( 
Bem, você e eu tiramos a seguinte conclusão. Escrevemos uma estrutura para um aplicativo GUI que cria exatamente os elementos de interface apropriados para um determinado sistema operacional. Vamos repetir brevemente o que criamos:
  • Família de produtos: campo de entrada, campo de seleção e botão.
  • Diversas implementações desta família de produtos, para Windows e macOS.
  • Uma fábrica abstrata, dentro da qual definimos a interface para criação de nossos produtos.
  • Duas implementações da nossa fábrica, cada uma delas responsável pela criação de uma família específica de produtos.
  • Um formulário, uma classe Java cujos campos são elementos abstratos de interface que são inicializados no construtor com os valores necessários usando uma fábrica abstrata.
  • Classe de aplicação. Dentro dele criamos um formulário com o qual passamos ao construtor a implementação necessária de nossa fábrica.
Total: implementamos o padrão abstrato de fábrica.

Fábrica abstrata: instruções de uso

Abstract Factory é um padrão de design para gerenciar a criação de diferentes famílias de produtos sem estar vinculado a classes de produtos específicas. Ao usar este modelo, você deve:
  1. Defina as próprias famílias de produtos. Vamos supor que temos dois deles:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. Para cada produto da família, defina uma classe abstrata (interface). No nosso caso é:
    • 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 de criação para cada produto definidos no passo 2. No nosso caso, esses métodos serão:
    • ProductA createProductA();
    • ProductB createProductB();
  5. Crie implementações da fábrica abstrata para que cada implementação controle a criação de produtos da mesma família. Para isso, dentro de cada implementação da fábrica abstrata, é necessário implementar todos os métodos de criação, para que implementações concretas de produtos sejam criadas e retornadas dentro deles.
Abaixo está um diagrama UML que ilustra as instruções descritas acima: Padrões de Design: AbstractFactory - 3Agora vamos escrever o código para esta instrução:
// Определим общие интерфейсы продуктов
public interface ProductA {}
public interface ProductB {}

// Создадим различные реализации (семейства) наших продуктов
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}

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

// Создадим абстрактную фабрику
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
public class SpecificFactory1 implements AbstractFactory {

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

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

// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
public class SpecificFactory2 implements AbstractFactory {

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

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

Trabalho de casa

Para consolidar o material você pode fazer 2 coisas:
  1. Melhore o aplicativo de pedido de café para que funcione no Linux.
  2. Crie sua própria fábrica abstrata para produzir unidades de qualquer estratégia. Esta pode ser uma estratégia histórica com exércitos reais ou uma fantasia com orcs, anões e elfos. O principal é que você ache interessante. Seja criativo, poste pins no console e divirta-se aprendendo os padrões!
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION