Паттерн Factory
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 91 из 110.
Паттерн «Фабрика» (Factory pattern) делегирует создание объектов отдельному методу или классу, позволяя создавать объекты без указания их конкретного класса. Это отличается от фабричного конструктора, о котором вы узнали ранее — паттерн «Фабрика» представляет собой подход к проектированию, в котором логика создания централизована и может возвращать различные типы в зависимости от входных данных.
Рассмотрим сценарий, в котором вам нужно создавать различные типы уведомлений. Вместо того чтобы клиент решал, какой класс инстанцировать, это решение берет на себя фабрика:
abstract class Notification {
void send(String message);
}
class EmailNotification implements Notification {
@override
void send(String message) => print('Email: $message');
}
class SMSNotification implements Notification {
@override
void send(String message) => print('SMS: $message');
}
class NotificationFactory {
static Notification create(String type) {
switch (type) {
case 'email':
return EmailNotification();
case 'sms':
return SMSNotification();
default:
return EmailNotification();
}
}
}
void main() {
var notification = NotificationFactory.create('sms');
notification.send('Hello!'); // SMS: Hello!
}Клиентскому коду не нужно знать о EmailNotification или SMSNotification — он просто запрашивает у фабрики уведомление определенного типа. Это упрощает добавление новых типов уведомлений: вы изменяете только фабрику, а не каждое место, где создаются уведомления.
Используйте паттерн Фабрика, когда создание объектов включает сложную логику, когда вы хотите скрыть классы реализации от клиентов или когда конкретный тип создаваемого объекта зависит от условий времени выполнения.
Задание
ЛегкоДавайте создадим генератор документов, используя паттерн «Фабрика»! Вы создадите систему, которая производит различные типы документов (отчеты, счета и письма) через централизованную фабрику, чтобы клиентскому коду не нужно было знать о конкретных классах документов.
Вы организуете свой код в двух файлах:
document_factory.dart: Создайте здесь свою систему документов:- Абстрактный класс
Documentс методомgenerate(String content), который возвращаетString - Три конкретных класса, реализующих
Document:Report— его методgenerateвозвращает=== REPORT ===\n[content]\n=============Invoice— его методgenerateвозвращаетINVOICE\n--------\n[content]\nTotal DueLetter— его методgenerateвозвращаетDear Reader,\n[content]\nSincerely
- Класс
DocumentFactoryсо статическим методомcreate(String type), который возвращает соответствующийDocumentна основе строки типа:"report","invoice"или"letter". Для любого неизвестного типа возвращайтеLetterпо умолчанию
- Абстрактный класс
main.dart: Импортируйте вашу фабрику документов и продемонстрируйте, как фабрика создает различные типы документов без необходимости знания клиентом конкретных классов:- Используйте фабрику для создания отчета и выведите результат его генерации с контентом
Sales increased by 25% - Выведите пустую строку
- Используйте фабрику для создания счета и выведите результат его генерации с контентом
Widget x3 - $150 - Выведите пустую строку
- Используйте фабрику для создания письма и выведите результат его генерации с контентом
Thank you for your order
- Используйте фабрику для создания отчета и выведите результат его генерации с контентом
Обратите внимание, что ваш основной файл взаимодействует только с фабрикой и абстрактным типом Document — он никогда напрямую не создает объекты Report, Invoice или Letter!
Ожидаемый вывод:
=== REPORT ===
Sales increased by 25%
=============
INVOICE
--------
Widget x3 - $150
Total Due
Dear Reader,
Thank you for your order
SincerelyШпаргалка
Паттерн Фабрика (Factory pattern) делегирует создание объектов отдельному методу или классу, позволяя создавать объекты без указания их конкретного класса. Логика создания централизована и может возвращать различные типы в зависимости от входных данных.
Базовая структура паттерна Фабрика:
abstract class Notification {
void send(String message);
}
class EmailNotification implements Notification {
@override
void send(String message) => print('Email: $message');
}
class SMSNotification implements Notification {
@override
void send(String message) => print('SMS: $message');
}
class NotificationFactory {
static Notification create(String type) {
switch (type) {
case 'email':
return EmailNotification();
case 'sms':
return SMSNotification();
default:
return EmailNotification();
}
}
}
void main() {
var notification = NotificationFactory.create('sms');
notification.send('Hello!'); // SMS: Hello!
}Клиентский код взаимодействует только с фабрикой и абстрактным типом, а не с конкретными классами реализации. Это упрощает добавление новых типов — вам нужно изменить только фабрику.
Используйте паттерн Фабрика, когда:
- Создание объекта включает сложную логику
- Вы хотите скрыть классы реализации от клиентов
- Конкретный тип для создания зависит от условий во время выполнения
Попробуйте сами
import 'document_factory.dart';
void main() {
// TODO: Используйте DocumentFactory.create() для создания отчета (report)
// Затем выведите результат его генерации с содержимым: "Sales increased by 25%"
// TODO: Выведите пустую строку
// TODO: Используйте DocumentFactory.create() для создания счета (invoice)
// Затем выведите результат его генерации с содержимым: "Widget x3 - \$150"
// TODO: Выведите пустую строку
// TODO: Используйте DocumentFactory.create() для создания письма (letter)
// Затем выведите результат его генерации с содержимым: "Thank you for your order"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий