BackendBit

К списку паттернов

Abstract Factory – интерфейс для создания семейства взаимосвязанных или взаимозависимых объектов, которые спроектированы для совместной работы, без указания их конкретных классов.

UML диаграмма классов паттерна Абстрактная фабрикаПоказывает интерфейс SmartHomeFactory с реализациями XiaomiSmartHomeFactory и SamsungSmartHomeFactory, интерфейсы продуктов RobotVacuumCleaner, SmartBulb, SmartSocket, а также конкретные продукты каждого производителя.Продукты XiaomiПродукты Samsung<<interface>>SmartHomeFactory+createRobotVacuumCleaner()+createSmartBulb()+createSmartSocket()<<interface>>RobotVacuumCleaner+clean()+isCleaning()<<interface>>SmartBulb+toggle()+isOn()<<interface>>SmartSocket+plug()+isOccupied()XiaomiSmartHomeFactorySamsungSmartHomeFactoryXiaomiRobotVacuumCleanerXiaomiSmartBulbXiaomiSmartSocketSamsungRobotVacuumCleanerSamsungSmartBulbSamsungSmartSocketВозвращаетпродукты XiaomiВозвращаетпродукты SamsungВозвращаетВозвращаетВозвращает

Пример

Представим, что наше приложение работает с умным домом. Предположим, что в каждом умном доме должны присутствовать: робот-пылесос, умная лампочка и умная розетка.

Представим три этих продукта в виде интерфейсов.

package smarthome;

public interface SmartDevice {
    String getVendor();
}
java
package smarthome;

public interface RobotVacuumCleaner extends SmartDevice {
    void clean();

    boolean isCleaning();
}
java
package smarthome;

public interface SmartBulb extends SmartDevice {
    void toggle();

    boolean isOn();
}
java
package smarthome;

public interface SmartSocket extends SmartDevice {
    void plug(SmartDevice device);

    void unplug();

    boolean isOccupied();
}
java

У нас также есть два производителя вышеперечисленных устройств: Xiaomi и Samsung.

package smarthome;

public final class XiaomiRobotVacuumCleaner implements RobotVacuumCleaner {
    private boolean cleaning = false;

    @Override
    public void clean() {
        cleaning = true;
        System.out.println("Xiaomi vacuum: cleaning...");
    }

    @Override
    public boolean isCleaning() {
        return cleaning;
    }

    @Override
    public String getVendor() {
        return "Xiaomi";
    }
}
java
package smarthome;

public final class XiaomiSmartBulb implements SmartBulb {
    private boolean on = false;

    @Override
    public void toggle() {
        on = !on;
        System.out.println("Xiaomi bulb: " + (on ? "on" : "off"));
    }

    @Override
    public boolean isOn() {
        return on;
    }

    @Override
    public String getVendor() {
        return "Xiaomi";
    }
}
java
package smarthome;

public final class XiaomiSmartSocket implements SmartSocket {
    private SmartDevice pluggedDevice;

    @Override
    public void plug(SmartDevice device) {
        pluggedDevice = device;
        System.out.println("Xiaomi socket: device plugged");
    }

    @Override
    public void unplug() {
        pluggedDevice = null;
        System.out.println("Xiaomi socket: device unplugged");
    }

    @Override
    public boolean isOccupied() {
        return pluggedDevice != null;
    }

    @Override
    public String getVendor() {
        return "Xiaomi";
    }
}
java

Реализации Samsung выглядят аналогично – они возвращают "Samsung" из метода getVendor().

Для максимальной синергии между устройствами мы хотим, чтобы все три продукта в доме были от одного производителя. Устройства одного вендора могут общаться друг с другом через проприетарные протоколы, синхронизировать состояние, отображаться в одном приложении. Как выразить это в коде? Создадим абстрактную фабрику.

package smarthome;

public interface SmartHomeFactory {
    RobotVacuumCleaner createRobotVacuumCleaner();

    SmartBulb createSmartBulb();

    SmartSocket createSmartSocket();
}
java

Итак, наша фабрика группирует создание семейства объектов – элементов умного дома. Остался финальный шаг: написать конкретные фабрики для Samsung и Xiaomi.

package smarthome;

public final class XiaomiSmartHomeFactory implements SmartHomeFactory {
    @Override
    public RobotVacuumCleaner createRobotVacuumCleaner() {
        return new XiaomiRobotVacuumCleaner();
    }

    @Override
    public SmartBulb createSmartBulb() {
        return new XiaomiSmartBulb();
    }

    @Override
    public SmartSocket createSmartSocket() {
        return new XiaomiSmartSocket();
    }
}
java
package smarthome;

public final class SamsungSmartHomeFactory implements SmartHomeFactory {
    @Override
    public RobotVacuumCleaner createRobotVacuumCleaner() {
        return new SamsungRobotVacuumCleaner();
    }

    @Override
    public SmartBulb createSmartBulb() {
        return new SamsungSmartBulb();
    }

    @Override
    public SmartSocket createSmartSocket() {
        return new SamsungSmartSocket();
    }
}
java

Теперь код может ссылаться на абстрактную фабрику умного дома (интерфейс SmartHomeFactory). Клиентскому коду не важно, с каким конкретно поставщиком умного дома он будет работать. При этом с помощью конкретных фабрик Samsung и Xiaomi мы можем быть уверены, что код, работая с интерфейсом SmartHomeFactory, всегда будет создавать устройства от одного производителя.

package smarthome;

public class SmartHomeApp {
    private final SmartHomeFactory factory;

    public SmartHomeApp(SmartHomeFactory factory) {
        this.factory = factory;
    }

    public void setupHome() {
        RobotVacuumCleaner vacuum = factory.createRobotVacuumCleaner();
        SmartBulb bulb = factory.createSmartBulb();
        SmartSocket socket = factory.createSmartSocket();

        // Все устройства гарантированно от одного производителя
        // и могут взаимодействовать друг с другом
        System.out.println("All devices from: " + vacuum.getVendor());

        socket.plug(bulb);
        vacuum.clean();
        bulb.toggle();
    }

    public static void main(String[] args) {
        // Легко переключиться между производителями
        SmartHomeFactory factory = new XiaomiSmartHomeFactory();
        // SmartHomeFactory factory = new SamsungSmartHomeFactory();

        SmartHomeApp app = new SmartHomeApp(factory);
        app.setupHome();
    }
}
java

Отличие от Factory Method

Factory Method и Abstract Factory – родственные паттерны, которые легко спутать.

Factory Method – это один метод в базовом классе или интерфейсе. Подклассы (или реализации интерфейса) переопределяют этот метод, чтобы создавать конкретную реализацию продукта.

Abstract Factory – это интерфейс с несколькими фабричными методами для создания семейства связанных продуктов. Ключевое отличие: продукты спроектированы для совместной работы.

package fmcomparison;

import java.util.Iterator;
import smarthome.*;

// Factory Method: один метод в базовом классе
abstract class TransactionProcessor {
    protected abstract Transaction createTransaction();
}

// Тоже Factory Method: метод в интерфейсе
interface Iterable<T> {
    Iterator<T> iterator();
}

// А это уже Abstract Factory: интерфейс с несколькими методами
// Методы создают продукты из одного семейства (здесь это умные устройства)
interface SmartHomeFactory {
    RobotVacuumCleaner createRobotVacuumCleaner();
    SmartBulb createSmartBulb();
    SmartSocket createSmartSocket();
}
java

Если нужно создать один объект и логика создания варьируется в подклассах – используйте Factory Method. Если нужно создать группу объектов, которые должны работать вместе – используйте Abstract Factory.

Реальные примеры

JDBC Connection

В JDBC паттерн Abstract Factory реализован через интерфейс Connection. Connection – это абстрактная фабрика, которая создает семейство связанных объектов для работы с базой данных: Statement, PreparedStatement, CallableStatement.

import java.sql.Connection;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.CallableStatement;

public class JdbcExample {
    public void demonstrateAbstractFactory(Connection conn) throws Exception {
        // Connection – абстрактная фабрика
        // Создает семейство связанных продуктов:

        // Продукт 1: простой Statement для статических запросов
        Statement stmt = conn.createStatement();

        // Продукт 2: PreparedStatement для параметризованных запросов
        PreparedStatement pstmt = conn.prepareStatement(
            "SELECT * FROM users WHERE id = ?"
        );

        // Продукт 3: CallableStatement для хранимых процедур
        CallableStatement cstmt = conn.prepareCall(
            "{call get_user_by_id(?)}"
        );

        // Все три продукта совместимы друг с другом и с данным Connection.
        // MySQL Connection создаст MySQL-совместимые Statement'ы,
        // PostgreSQL Connection – PostgreSQL-совместимые.
    }
}
java

Клиентский код работает с интерфейсом Connection и не зависит от конкретной базы данных. Можно легко переключиться между MySQL и PostgreSQL – все созданные Statement’ы будут корректно работать с выбранной СУБД.

Swing LookAndFeel

Классический пример Abstract Factory в стандартной библиотеке Java – система Look and Feel в Swing. LookAndFeel – это абстрактная фабрика, которая создаёт семейство визуально согласованных UI-компонентов.

import javax.swing.*;

public class LookAndFeelExample {
    public static void main(String[] args) throws Exception {
        // Выбираем "фабрику" UI-компонентов
        // Каждый LookAndFeel создаёт семейство согласованных компонентов

        // Вариант 1: Metal (кроссплатформенный стиль Java)
        UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");

        // Вариант 2: Nimbus (современный стиль)
        // UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");

        // Вариант 3: Системный стиль (Windows/macOS/GTK)
        // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        // Все компоненты автоматически получают согласованный внешний вид
        JFrame frame = new JFrame("Abstract Factory в Swing");
        JButton button = new JButton("Кнопка");
        JCheckBox checkBox = new JCheckBox("Чекбокс");
        JProgressBar progressBar = new JProgressBar(0, 100);

        // Внутри Swing: LookAndFeel регистрирует UI-делегаты для каждого компонента.
        // При создании компонента UIManager находит нужный класс и вызывает:
        // MetalButtonUI.createUI(button)       → экземпляр MetalButtonUI
        // MetalCheckBoxUI.createUI(checkBox)   → экземпляр MetalCheckBoxUI
        // и т.д.

        JPanel panel = new JPanel();
        panel.add(button);
        panel.add(checkBox);
        panel.add(progressBar);

        frame.add(panel);
        frame.setSize(300, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
java

Это каноничный пример Abstract Factory: одна фабрика (MetalLookAndFeel, NimbusLookAndFeel) создаёт целое семейство продуктов (кнопки, чекбоксы, скроллбары), которые спроектированы для совместной работы и визуально согласованы друг с другом.