Паттерн «Шаблонный метод»
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 73 из 87.
Template Method Pattern — это поведенческий шаблон проектирования, который определяет скелет алгоритма в базовом классе, позволяя подклассам переопределять отдельные шаги без изменения общей структуры. Представь это как рецепт: общий процесс приготовления остаётся тем же, но отдельные ингредиенты или техники могут различаться.
В этом шаблоне используется абстрактный класс с шаблонным методом, который вызывает последовательность шагов. Некоторые шаги имеют реализации по умолчанию, тогда как другие являются abstract и должны быть реализованы подклассами:
abstract class DataProcessor {
// Шаблонный метод — определяет структуру алгоритма
public final void process() {
readData();
processData();
saveData();
}
abstract void readData();
abstract void processData();
// Реализация по умолчанию (hook-метод)
void saveData() {
System.out.println("Saving to default location");
}
}
class CSVProcessor extends DataProcessor {
void readData() {
System.out.println("Reading CSV file");
}
void processData() {
System.out.println("Parsing CSV data");
}
}
class XMLProcessor extends DataProcessor {
void readData() {
System.out.println("Reading XML file");
}
void processData() {
System.out.println("Parsing XML data");
}
}Шаблонный метод обычно помечается как final, чтобы предотвратить изменение структуры алгоритма подклассами. При вызове он выполняет одну и ту же последовательность для всех подклассов:
DataProcessor csv = new CSVProcessor();
csv.process();
// Вывод:
// Чтение CSV файла
// Разбор CSV данных
// Сохранение в расположение по умолчаниюШаблонный метод идеален, когда у вас есть алгоритмы, которые имеют одинаковую структуру, но отличаются на определённых шагах. Он способствует повторному использованию кода, размещая общую логику в базовом классе и одновременно позволяя настраивать её с помощью наследования.
Задание
ЛегкоДавайте создадим систему генерации документов с использованием паттерна Template Method! Вы создадите framework, в котором разные типы документов (отчёты и счета) следуют одному и тому же процессу генерации, но настраивают отдельные шаги. Это идеально подходит для сценариев, где общий рабочий процесс фиксирован, но детали различаются в зависимости от типа документа.
Вы организуете код в четырёх файлах:
DocumentGenerator.java: Создайте абстрактный базовый class, который определяет шаблонный метод. ВашDocumentGeneratorдолжен содержать:Метод
finalс именемgenerateDocument(), который определяет каркас algorithm, вызывая три шага по порядку:createHeader(),createBody()иcreateFooter().Два абстрактных метода:
createHeader()иcreateBody(), которые подклассы должны реализовать.Метод hook
createFooter()с реализацией по умолчанию, которая выводит--- End of Document ---.ReportGenerator.java: Создайте конкретный подкласс для генерации отчётов. Он должен получать заголовок отчёта (String) через свой constructor.Метод
createHeader()выводит=== REPORT: [title] ===Метод
createBody()выводитReport content goes here...Используйте унаследованный footer по умолчанию.
InvoiceGenerator.java: Создайте ещё один конкретный подкласс для генерации счетов. Он должен получать имя Customer (String) и сумму (double) через свой constructor.Метод
createHeader()выводит*** INVOICE ***Метод
createBody()выводитCustomer: [name]в одной строке, а затемAmount Due: $[amount]в следующей строке (сумма форматируется с двумя десятичными знаками).Переопределите
createFooter(), чтобы выводитьThank you for your business!Main.java: Объедините вашу систему шаблонного метода! Вы получите три входных значения: заголовок отчёта (String), имя Customer (String) и сумму счёта (double).Создайте
ReportGeneratorс этим заголовком и вызовитеgenerateDocument(). Выведите пустую строку для разделения. Затем создайтеInvoiceGeneratorс именем Customer и суммой и вызовитеgenerateDocument().
Вы получите три входных значения в следующем порядке: заголовок отчёта (String), имя Customer (String) и сумму счёта (double).
Например, если ввести Q4 Sales, Acme Corp и 1500.00, результатом будет:
=== REPORT: Q4 Sales ===
Report content goes here...
--- End of Document ---
*** INVOICE ***
Customer: Acme Corp
Amount Due: $1500.00
Thank you for your business!Обратите внимание, что оба типа документов следуют одному и тому же процессу из трёх шагов, определённому в шаблонном методе, но каждый по-разному настраивает эти шаги. ReportGenerator использует footer по умолчанию, тогда как InvoiceGenerator предоставляет собственную реализацию. В этом заключается сила hook-методов в паттерне Template Method!
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Читаем входные данные
String reportTitle = scanner.nextLine();
String customerName = scanner.nextLine();
double invoiceAmount = scanner.nextDouble();
// TODO: Создайте ReportGenerator с заголовком и вызовите generateDocument()
// TODO: Выведите пустую строку для разделения
// TODO: Создайте InvoiceGenerator с именем клиента и суммой и вызовите generateDocument()
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java