Паттерн Factory
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 66 из 87.
Паттерн Фабрика (Factory Pattern) — это порождающий паттерн проектирования, который делегирует создание объектов отдельному методу или классу. Вместо того чтобы использовать new напрямую в вашем коде, вы просите фабрику создать объекты за вас. Это особенно полезно, когда у вас есть несколько связанных классов, которые имеют общий интерфейс или родителя.
Рассмотрим систему уведомлений, которая может отправлять электронные письма, SMS или push-уведомления. Без фабрики вашему коду пришлось бы знать о каждом конкретном классе:
// Без фабрики — клиент должен знать все конкретные классы
Notification notification;
if (type.equals("email")) {
notification = new EmailNotification();
} else if (type.equals("sms")) {
notification = new SMSNotification();
}С помощью паттерна Фабрика вы централизуете логику создания:
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
public void send(String message) {
System.out.println("Email: " + message);
}
}
public class SMSNotification implements Notification {
public void send(String message) {
System.out.println("SMS: " + message);
}
}
public class NotificationFactory {
public static Notification create(String type) {
if (type.equals("email")) {
return new EmailNotification();
} else if (type.equals("sms")) {
return new SMSNotification();
}
return null;
}
}Теперь клиентский код стал чище и не зависит от конкретных классов:
Notification notification = NotificationFactory.create("email");
notification.send("Hello!"); // Вывод: Email: Hello!Ключевое преимущество заключается в том, что добавление новых типов уведомлений требует только обновления фабрики — клиентский код остается неизменным. Это следует принципу программирования на уровне интерфейса, что делает вашу систему более гибкой и простой в расширении.
Задание
ЛегкоДавайте создадим систему рисования фигур, используя паттерн «Фабрика» (Factory Pattern)! Вместо того чтобы создавать объекты фигур напрямую по всему коду, вы централизуете логику создания в фабрике, которая производит различные типы фигур на основе простого строкового идентификатора.
Вы организуете свой код в четырех файлах:
Shape.java: Определите интерфейс с именемShape, который служит общим контрактом для всех фигур. Ваш интерфейс должен объявлять один методdraw(), который ничего не возвращает, но выводит информацию о рисуемой фигуре.Shapes.java: Создайте три класса, реализующих ваш интерфейсShape:Circle— его методdraw()должен выводитьDrawing a CircleRectangle— его методdraw()должен выводитьDrawing a RectangleTriangle— его методdraw()должен выводитьDrawing a TriangleShapeFactory.java: Создайте класс фабрики, который управляет созданием фигур. ВашShapeFactoryдолжен иметь статический методcreateShape(String type), который возвращаетShape. В зависимости от параметра типа:- Если тип равен
"circle", верните новыйCircle - Если тип равен
"rectangle", верните новыйRectangle - Если тип равен
"triangle", верните новыйTriangle - Для любого другого значения верните
null
- Если тип равен
Main.java: Объедините вашу систему фабрик! Вы получите два входных значения: два типа фигур (обе строки).Для каждого входного значения используйте
ShapeFactory, чтобы создать фигуру и сохранить ее в переменной типаShape. Если фабрика возвращает валидную фигуру (не null), вызовите ее методdraw(). Если она возвращает null, выведитеUnknown shape: [type], где [type] — это предоставленное входное значение.Обработайте оба входных значения по порядку, каждое на отдельной строке вывода.
Вы получите два входных значения по порядку: первый тип фигуры (String) и второй тип фигуры (String).
Обратите внимание, что ваш класс Main никогда не использует new Circle() или new Rectangle() напрямую — он знает только об интерфейсе Shape и просит фабрику создать объекты. В этом и заключается сила паттерна «Фабрика»: клиентский код отделен от конкретных классов!
Шпаргалка
Паттерн «Фабрика» (Factory Pattern) — это порождающий паттерн проектирования, который делегирует создание объектов отдельному методу или классу, централизуя логику создания и отделяя клиентский код от конкретных реализаций.
Вместо того чтобы создавать экземпляры объектов напрямую с помощью new, вы используете фабричный метод:
// Без фабрики — клиент зависит от конкретных классов
Notification notification;
if (type.equals("email")) {
notification = new EmailNotification();
} else if (type.equals("sms")) {
notification = new SMSNotification();
}С паттерном «Фабрика» определите общий интерфейс и конкретные реализации:
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
public void send(String message) {
System.out.println("Email: " + message);
}
}
public class SMSNotification implements Notification {
public void send(String message) {
System.out.println("SMS: " + message);
}
}Создайте класс фабрики со статическим методом создания:
public class NotificationFactory {
public static Notification create(String type) {
if (type.equals("email")) {
return new EmailNotification();
} else if (type.equals("sms")) {
return new SMSNotification();
}
return null;
}
}Клиентский код использует фабрику вместо прямого создания экземпляров:
Notification notification = NotificationFactory.create("email");
notification.send("Hello!"); // Вывод: Email: Hello!Преимущества: Добавление новых типов требует обновления только фабрики — клиентский код остается неизменным. Это соответствует принципу программирования на уровне интерфейсов, что делает системы более гибкими и простыми в расширении.
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Прочитать два типа фигур
String type1 = scanner.nextLine();
String type2 = scanner.nextLine();
// TODO: Используйте ShapeFactory для создания первой фигуры
// Если фигура не null, вызовите draw()
// Если фигура null, выведите "Unknown shape: [type]"
// TODO: Сделайте то же самое для второй фигуры
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»