Abstract Factory (Абстрактная фабрика)
Abstract Factory – интерфейс для создания семейства взаимосвязанных или взаимозависимых объектов, которые спроектированы для совместной работы, без указания их конкретных классов.
Пример
Представим, что наше приложение работает с умным домом. Предположим, что в каждом умном доме должны присутствовать: робот-пылесос, умная лампочка и умная розетка.
Представим три этих продукта в виде интерфейсов.
package smarthome;
public interface SmartDevice {
String getVendor();
}javapackage smarthome;
public interface RobotVacuumCleaner extends SmartDevice {
void clean();
boolean isCleaning();
}javapackage smarthome;
public interface SmartBulb extends SmartDevice {
void toggle();
boolean isOn();
}javapackage 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";
}
}javapackage 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";
}
}javapackage 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();
}
}javapackage 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) создаёт целое семейство продуктов (кнопки, чекбоксы, скроллбары), которые спроектированы для совместной работы и визуально согласованы друг с другом.