Java: Абстрактна фабрика

Abstract Factory Абстрактна фабрика Abstract Factory

Абстрактна фабрика — це породжуючий патерн проектування, який вирішує проблему створення цілих сімейств пов'язаних продуктів, без прив'язки коду до конкретних класів продуктів.

Абстрактна фабрика задає інтерфейс створення всіх доступних типів продуктів, а кожна конкретна реалізація фабрики породжує продукти однієї з варіацій.

Клієнтський код викликає методи фабрики для отримання продуктів, замість самостійного створювання їх за допомогою оператора new. При цьому, фабрика сама стежить за тим, щоб створюваний продукт був потрібної варіації.

Клієнтський код працює з фабриками та продуктами тільки через загальні інтерфейси. Завдяки цьому, ви можете подавати в один і той же клієнтський код різні фабрики, не ламаючи його.

Якщо ви вже чули про Фабрику, Фабричний метод чи Абстрактну фабрику, але вам все одно важко їх розрізняти, то прочитайте нашу статтю Порівняння фабрик.

Детальніше про Абстрактну фабрику

Особливості паттерна в Java

Складність: Середня

Популярність: Висока

Застосування: Патерн можна часто зустріти в Java-коді, особливо там, де потрібно створити сімейства продуктів (наприклад, у фреймворках).

Приклади Абстрактної фабрики в стандартних бібліотеках Java:

Ознаки застосування патерну: Патерн можна визначити за методами, що повертають фабрику, яка, в свою чергу, використовується для створення конкретних продуктів, повертаючи їх через абстрактні типи або інтерфейси.

Приклад: Виробництво сімейств крос-платформних елементів GUI

У цьому прикладі в ролі двох сімейств продуктів виступають кнопки та чекбокси. Обидва сімейства продуктів мають однакові варіації: для роботи під MacOS та Windows.

Абстрактна фабрика задає інтерфейс створення продуктів усіх сімейств. Конкретні фабрики створюють різні продукти однієї варіації (MacOS або Windows).

Клієнти фабрики працюють як з фабрикою, так і з продуктами тільки через абстрактні інтерфейси. Завдяки цьому, один і той же клієнтський код може працювати з різними фабриками та продуктами.

buttons: Перша ієрархія продуктів (кнопки)

buttons/Button.java

package refactoring_guru.abstract_factory.example.buttons;

/**
 * Abstract Factory assumes that you have several families of products,
 * structured into separate class hierarchies (Button/Checkbox). All products of
 * the same family have the common interface.
 * 
 * This is the common interface for buttons family.
 */
public interface Button {
    public abstract void paint();
}

buttons/MacOSButton.java

package refactoring_guru.abstract_factory.example.buttons;

/**
 * All products families have the same varieties (MacOS/Windows).
 * 
 * This is a MacOS variant of a button.
 */
public class MacOSButton implements Button {

    @Override
    public void paint() {
        System.out.println("You have created MacOSButton.");
    }
}

buttons/WindowsButton.java

package refactoring_guru.abstract_factory.example.buttons;

/**
 * All products families have the same varieties (MacOS/Windows).
 * 
 * This is another variant of a button.
 */
public class WindowsButton implements Button {

    @Override
    public void paint() {
        System.out.println("You have created WindowsButton.");
    }
}

checkboxes: Друга ієрархія продуктів (чекбокси)

checkboxes/Checkbox.java

package refactoring_guru.abstract_factory.example.checkboxes;

/**
 * Checkboxes is the second product family. It has the same variants as buttons.
 */
public interface Checkbox {
    public abstract void paint();
}

checkboxes/MacOSCheckbox.java

package refactoring_guru.abstract_factory.example.checkboxes;

/**
 * All products families have the same varieties (MacOS/Windows).
 * 
 * This is a variant of a checkbox.
 */
public class MacOSCheckbox implements Checkbox {

    @Override
    public void paint() {
        System.out.println("You have created MacOSCheckbox.");
    }
}

checkboxes/WindowsCheckbox.java

package refactoring_guru.abstract_factory.example.checkboxes;

/**
 * All products families have the same varieties (MacOS/Windows).
 * 
 * This is another variant of a checkbox.
 */
public class WindowsCheckbox implements Checkbox {

    @Override
    public void paint() {
        System.out.println("You have created WindowsCheckbox.");
    }
}

factories

factories/GUIFactory.java: Абстрактна фабрика

package refactoring_guru.abstract_factory.example.factories;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;

/**
 * Abstract factory knows about all (abstract) product types.
 */
public interface GUIFactory {
    public abstract Button createButton();
    public abstract Checkbox createCheckbox();
}

factories/MacOSFactory.java: Конкретна фабрика (MacOS)

package refactoring_guru.abstract_factory.example.factories;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.buttons.MacOSButton;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;
import refactoring_guru.abstract_factory.example.checkboxes.MacOSCheckbox;

/**
 * Each concrete factory extends basic factory and responsible for creating
 * products of a single variety.
 */
public class MacOSFactory implements GUIFactory {

    @Override
    public Button createButton() {
        return new MacOSButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new MacOSCheckbox();
    }
}

factories/WindowsFactory.java: Конкретна фабрика (Windows)

package refactoring_guru.abstract_factory.example.factories;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.checkboxes.WindowsCheckbox;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;
import refactoring_guru.abstract_factory.example.buttons.WindowsButton;

/**
 * Each concrete factory extends basic factory and responsible for creating
 * products of a single variety.
 */
public class WindowsFactory implements GUIFactory {

    @Override
    public Button createButton() {
        return new WindowsButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

app

app/Application.java: Клієнтський код

package refactoring_guru.abstract_factory.example.app;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;
import refactoring_guru.abstract_factory.example.factories.GUIFactory;

/**
 * Factory users don't care which concrete factory they use since they work with
 * factories and products through abstract interfaces.
 */
public class Application {
    private Button button;
    private Checkbox checkbox;

    public Application(GUIFactory factory) {
        button = factory.createButton();
        checkbox = factory.createCheckbox();
    }

    public void paint() {
        button.paint();
        checkbox.paint();
    }
}

Demo.java: Конфігуратор програми

package refactoring_guru.abstract_factory.example;

import refactoring_guru.abstract_factory.example.app.Application;
import refactoring_guru.abstract_factory.example.factories.GUIFactory;
import refactoring_guru.abstract_factory.example.factories.MacOSFactory;
import refactoring_guru.abstract_factory.example.factories.WindowsFactory;

/**
 * Demo class. Everything comes together here.
 */
public class Demo {

    /**
     * Application picks the factory type and creates it in run time (usually at
     * initialization stage), depending on the configuration or
     * environment variables.
     */
    private static Application configureApplication() {
        Application app;
        GUIFactory factory;
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.contains("mac")) {
            factory = new MacOSFactory();
            app = new Application(factory);
        } else {
            factory = new WindowsFactory();
            app = new Application(factory);
        }
        return app;
    }

    public static void main(String[] args) {
        Application app = configureApplication();
        app.paint();
    }
}

OutputDemo.txt: Результат виконання

You create WindowsButton.
You created WindowsCheckbox.