Паттерн Шаблонный метод
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy. Урок 97 из 110.
Паттерн Template Method defines скелет algorithm в base class, позволяя подклассам переопределять отдельные шаги без изменения общей структуры. Base class управляет рабочим процессом, а подклассы настраивают отдельные шаги.
Этот паттерн использует наследование: abstract class defines шаблонный метод, который вызывает последовательность шагов; некоторые из них являются абстрактными (их должны implement подклассы), а для некоторых есть реализации по умолчанию:
abstract class DataProcessor {
// Шаблонный метод - определяет структуру алгоритма
void process() {
readData();
processData();
saveData();
}
void readData(); // Абстрактный - подклассы должны реализовать
void processData(); // Абстрактный - подклассы должны реализовать
void saveData() { // Реализация по умолчанию
print('Data saved to database');
}
}
class CsvProcessor extends DataProcessor {
@override
void readData() => print('Reading CSV file');
@override
void processData() => print('Parsing CSV rows');
}
class JsonProcessor extends DataProcessor {
@override
void readData() => print('Reading JSON file');
@override
void processData() => print('Parsing JSON objects');
@override
void saveData() => print('Saved to cloud storage');
}
void main() {
var csv = CsvProcessor();
csv.process();
// Чтение CSV файла
// Разбор строк CSV
// Данные сохранены в базу данных
var json = JsonProcessor();
json.process();
// Чтение JSON файла
// Разбор объектов JSON
// Сохранено в облачное хранилище
}Метод process() — это шаблон: он defines порядок операций. Подклассы реализуют readData() и processData() differently и могут при необходимости переопределить saveData(). Главное преимущество заключается в том, что структура algorithm остаётся согласованной во всех реализациях, обеспечивая гибкость отдельных шагов.
Задание
ЛегкоДавайте создадим систему генерации отчётов с использованием шаблона Template Method! Вы создадите framework, в котором разные типы отчётов следуют одной общей структуре, но каждый тип отчёта настраивает определённые шаги, такие как сбор data и форматирование результата.
Вы организуете код в два файла:
report_generator.dart: Этот файл содержит ваш abstract базовый класс и Concrete реализации отчётов. Создайте abstract классReportGeneratorс шаблонным методомgenerate(), который определяет workflow генерации отчёта в следующем порядке:fetchData(),analyzeData(),formatReport()иdeliver(). МетодыfetchData()иformatReport()должны быть abstract (каждый тип отчёта обрабатывает их differently), аanalyzeData()должен выводитьAnalyzing data..., иdeliver()должен выводитьReport delivered via email.в качестве Default реализаций. Затем создайте два Concrete класса отчётов:SalesReport— егоfetchData()выводитFetching sales data from database..., а егоformatReport()выводитFormatting as bar charts and tables.InventoryReport— егоfetchData()выводитFetching inventory levels from warehouse..., егоformatReport()выводитFormatting as inventory list., а сам класс переопределяетdeliver(), чтобы выводитьReport sent to warehouse manager.
main.dart: Импортируйте файл генератора отчётов и продемонстрируйте, как шаблон Template Method сохраняет согласованной структуру algorithm, позволяя выполнять настройку. Создайте экземплярSalesReportи вызовите его методgenerate(). Затем выведите пустую строку для разделения. Создайте экземплярInventoryReportи также вызовите его методgenerate().
Обратите внимание, что оба отчёта следуют одному и тому же процессу из четырёх шагов, но каждый настраивает шаги, важные для его конкретного назначения. InventoryReport даже переопределяет Default метод доставки, чтобы отправлять отчёты другому получателю!
Ожидаемый результат:
Fetching sales data from database...
Analyzing data...
Formatting as bar charts and tables.
Report delivered via email.
Fetching inventory levels from warehouse...
Analyzing data...
Formatting as inventory list.
Report sent to warehouse manager.Попробуйте сами
import 'report_generator.dart';
void main() {
// TODO: Создайте SalesReport и вызовите его метод generate()
// TODO: Выведите пустую строку для разделения
// TODO: Создайте InventoryReport и вызовите его метод generate()
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн РепозиторийПотренируйтесь самостоятельно: Онлайн-компилятор Dart