Menu
Coddy logo textTech

Паттерн «Шаблонный метод»

Часть раздела Объектно-ориентированное программирование путешествия по 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 данных
// Сохранение в расположение по умолчанию

Шаблонный метод идеален, когда у вас есть алгоритмы, которые имеют одинаковую структуру, но отличаются на определённых шагах. Он способствует повторному использованию кода, размещая общую логику в базовом классе и одновременно позволяя настраивать её с помощью наследования.

challenge icon

Задание

Легко

Давайте создадим систему генерации документов с использованием паттерна 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()
        
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Java