Шаблон проектирования «Фабричный метод
Фабричный метод (англ. Factory Method) — это порождающий шаблон проектирования, который предоставляет интерфейс для создания объектов в суперклассе, но позволяет подклассам изменять тип создаваемых объектов. Шаблон решает проблему создания объектов без указания их конкретных классов, делегируя ответственность за инстанцирование наследникам. Фабричный метод является одним из классических паттернов, описанных в книге «Банды четырёх» (GoF) и широко применяется в объектно-ориентированном программировании.
История и происхождение
Шаблон «Фабричный метод» был впервые систематизирован и описан в 1994 году в книге «Design Patterns: Elements of Reusable Object-Oriented Software» (рус. «Приёмы объектно-ориентированного проектирования. Паттерны проектирования»), авторами которой выступили Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес (известные как «Банда четырёх», GoF). В книге шаблон отнесён к категории порождающих паттернов, поскольку он абстрагирует процесс создания объектов.
Идея фабричного метода восходит к более ранним концепциям, таким как «виртуальный конструктор» (virtual constructor) в языках C++ и Smalltalk. В языке Java шаблон активно используется в стандартной библиотеке, например, в методе iterator() интерфейса Collection, который возвращает объект-итератор конкретного типа. В последующие десятилетия шаблон получил широкое распространение в корпоративной разработке, особенно в среде Java и .NET, а также в языках с поддержкой полиморфизма.
Проблема, решаемая шаблоном
Основная проблема, которую решает фабричный метод, — это необходимость создания объектов, конкретный тип которых заранее неизвестен или может изменяться в зависимости от контекста. В традиционном подходе код напрямую использует оператор new для создания экземпляра конкретного класса. Это приводит к жёсткой связанности (tight coupling) между клиентским кодом и конкретными классами, что затрудняет расширение и модификацию системы.
Пример типичной проблемы: приложение для логистики должно поддерживать разные виды транспорта (грузовики, корабли, самолёты). Если код создания транспорта разбросан по всему приложению, добавление нового вида транспорта потребует изменения множества мест. Фабричный метод централизует логику создания и позволяет добавлять новые типы, не изменяя существующий клиентский код.
Структура шаблона
Шаблон «Фабричный метод» состоит из четырёх основных компонентов:
- Продукт (Product) — абстрактный класс или интерфейс, определяющий общий для всех создаваемых объектов тип. Например, интерфейс
Transportс методомdeliver(). - Конкретный продукт (ConcreteProduct) — конкретные классы, реализующие интерфейс продукта. Например,
TruckиShip. - Создатель (Creator) — абстрактный класс или интерфейс, объявляющий фабричный метод, который возвращает объект типа
Product. Создатель может содержать базовую реализацию фабричного метода, возвращающую некоторый продукт по умолчанию. - Конкретный создатель (ConcreteCreator) — подклассы создателя, которые переопределяют фабричный метод для создания конкретных продуктов.
Схема взаимодействия
Клиентский код взаимодействует с объектом-создателем через общий интерфейс. При вызове фабричного метода создатель возвращает конкретный продукт. Клиент не знает, какой именно подкласс продукта был создан, и работает с ним через интерфейс продукта.
Реализация на примере
Рассмотрим реализацию фабричного метода на языке Java на примере логистической системы.
```java // Интерфейс продукта interface Transport { void deliver(); }
// Конкретные продукты class Truck implements Transport { public void deliver() { System.out.println("Доставка грузовиком по земле"); } }
class Ship implements Transport { public void deliver() { System.out.println("Доставка кораблём по морю"); } }
// Абстрактный создатель abstract class Logistics { // Фабричный метод abstract Transport createTransport();
// Бизнес-логика, использующая фабричный метод public void planDelivery() { Transport transport = createTransport(); transport.deliver(); } }
// Конкретные создатели class RoadLogistics extends Logistics { Transport createTransport() { return new Truck(); } }
class SeaLogistics extends Logistics { Transport createTransport() { return new Ship(); } }
// Клиентский код public class Main { public static void main(String[] args) { Logistics logistics = new RoadLogistics(); logistics.planDelivery(); // Вывод: Доставка грузовиком по земле
logistics = new SeaLogistics(); logistics.planDelivery(); // Вывод: Доставка кораблём по морю } } ```
В данном примере фабричный метод createTransport() определён в абстрактном классе Logistics. Подклассы RoadLogistics и SeaLogistics переопределяют этот метод, возвращая соответствующие конкретные продукты. Клиентский код не зависит от конкретных классов Truck или Ship, а только от абстракции Transport.
Применение
Шаблон «Фабричный метод» применяется в следующих ситуациях:
- Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать код.
- Когда система должна быть независимой от процесса создания новых типов объектов.
- Когда необходимо предоставить пользователям библиотеки или фреймворка способ расширения её внутренних компонентов.
- Когда нужно переиспользовать существующие объекты вместо создания новых (например, пул объектов).
В современной разработке фабричный метод часто используется в комбинации с другими шаблонами, такими как «Стратегия» или «Шаблонный метод». Он является основой для многих фреймворков, например, в Spring Framework для создания бинов, в Java Swing для создания компонентов UI, а также в библиотеках для работы с базами данных (JDBC, Hibernate).
Отличия от других порождающих шаблонов
Фабричный метод часто путают с другими порождающими шаблонами, особенно с Абстрактной фабрикой (Abstract Factory) и Прототипом (Prototype). Основные различия:
- Фабричный метод — создаёт один объект через наследование. Подкласс решает, какой конкретный класс инстанцировать.
- Абстрактная фабрика — создаёт семейства связанных объектов через композицию. Она использует несколько фабричных методов для создания разных типов продуктов.
- Прототип — создаёт объекты путём клонирования существующего экземпляра, а не через вызов конструктора.
Фабричный метод также отличается от простой фабрики (Simple Factory), которая не является полноценным шаблоном GoF. Простая фабрика — это статический метод, который создаёт объекты на основе переданного параметра, но не поддерживает полиморфное расширение через наследование.
Преимущества и недостатки
Преимущества
- Уменьшение связанности: клиентский код не зависит от конкретных классов продуктов.
- Принцип открытости/закрытости: можно добавлять новые типы продуктов, не изменяя существующий клиентский код.
- Единая точка создания: логика создания объектов централизована, что упрощает её изменение и тестирование.
- Поддержка полиморфизма: клиент работает с продуктами через общий интерфейс.
Недостатки
- Усложнение кода: введение дополнительных классов и интерфейсов может сделать код более громоздким, особенно для простых случаев.
- Необходимость наследования: шаблон требует создания подклассов для каждого нового типа продукта, что может привести к росту числа классов.
- Ограниченная гибкость: если требуется создавать объекты с разными параметрами, фабричный метод может потребовать дополнительных механизмов (например, передачи параметров в фабричный метод).
Критика
В современной разработке фабричный метод иногда критикуется за излишнюю сложность в простых системах. Для случаев, когда типы объектов известны на этапе компиляции и не меняются, использование фабричного метода может быть избыточным. Кроме того, в языках с поддержкой функционального программирования (например, в Java 8+ с лямбда-выражениями) или в языках с динамической типизацией (Python, JavaScript) шаблон может быть реализован более лаконично без создания иерархии классов.
Тем не менее, фабричный метод остаётся одним из фундаментальных инструментов объектно-ориентированного проектирования и входит в стандартный арсенал разработчика при создании расширяемых и поддерживаемых систем.
Источники
- Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. «Приёмы объектно-ориентированного проектирования. Паттерны проектирования» (Design Patterns: Elements of Reusable Object-Oriented Software), 1994.
- Фримен Э., Фримен Э., Сьерра К., Бейтс Б. «Паттерны проектирования» (Head First Design Patterns), 2004.
- Мартин Р. «Чистый код: создание, анализ и рефакторинг» (Clean Code: A Handbook of Agile Software Craftsmanship), 2008.
- Официальная документация Oracle по шаблонам проектирования в Java (Java Design Patterns).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →