Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 73 из 87.

Шаблонный метод (Template Method Pattern) — это поведенческий паттерн проектирования, который определяет скелет алгоритма в базовом классе, позволяя подклассам переопределять конкретные шаги, не изменяя общую структуру. Представьте это как рецепт: общий процесс приготовления остается неизменным, но отдельные ингредиенты или техники могут варьироваться.

Этот паттерн использует абстрактный класс с шаблонным методом, который вызывает последовательность шагов. Некоторые шаги имеют реализации по умолчанию, в то время как другие являются абстрактными и должны быть реализованы подклассами:

abstract class DataProcessor {
    // Шаблонный метод — определяет структуру алгоритма
    public final void process() {
        readData();
        processData();
        saveData();
    }
    
    abstract void readData();
    abstract void processData();
    
    // Реализация по умолчанию (метод-хук)
    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 Pattern) идеально подходит в тех случаях, когда у вас есть алгоритмы, имеющие одинаковую структуру, но различающиеся в конкретных шагах. Он способствует повторному использованию кода, размещая общую логику в базовом классе и позволяя настраивать её через наследование.

challenge icon

Задание

Легко

Давайте создадим систему генерации документов, используя паттерн «Шаблонный метод» (Template Method Pattern)! Вы создадите структуру, в которой различные типы документов (отчеты и счета) следуют одному и тому же процессу генерации, но настраивают конкретные шаги под себя. Это идеально подходит для сценариев, где общий рабочий процесс фиксирован, но детали различаются в зависимости от типа документа.

Вы организуете свой код в четырех файлах:

  • DocumentGenerator.java: Создайте абстрактный базовый класс, который определяет шаблонный метод. Ваш DocumentGenerator должен содержать:

    Метод final с именем generateDocument(), который определяет скелет алгоритма, вызывая по порядку три шага: createHeader(), createBody() и createFooter().

    Два абстрактных метода: createHeader() и createBody(), которые должны реализовать подклассы.

    Метод-перехватчик (hook) createFooter() с реализацией по умолчанию, которая выводит --- End of Document ---.

  • ReportGenerator.java: Создайте конкретный подкласс для генерации отчетов. Он должен принимать заголовок отчета (String) через конструктор.

    Метод createHeader() выводит === REPORT: [title] ===

    Метод createBody() выводит Report content goes here...

    Используйте унаследованный нижний колонтитул по умолчанию.

  • InvoiceGenerator.java: Создайте еще один конкретный подкласс для генерации счетов. Он должен принимать имя клиента (String) и сумму (double) через конструктор.

    Метод createHeader() выводит *** INVOICE ***

    Метод createBody() выводит Customer: [name] на одной строке, затем Amount Due: $[amount] на следующей строке (отформатируйте сумму до двух знаков после запятой).

    Переопределите createFooter(), чтобы вывести Thank you for your business!

  • Main.java: Объедините вашу систему шаблонных методов! Вы получите три входных значения: заголовок отчета (String), имя клиента (String) и сумму счета (double).

    Создайте ReportGenerator с заголовком и вызовите generateDocument(). Выведите пустую строку для разделения. Затем создайте InvoiceGenerator с именем клиента и суммой и вызовите generateDocument().

Вы получите три входных значения по порядку: заголовок отчета (String), имя клиента (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 использует нижний колонтитул по умолчанию, в то время как InvoiceGenerator предоставляет свой собственный — в этом и заключается сила методов-перехватчиков в паттерне «Шаблонный метод»!

Шпаргалка

Шаблонный метод (Template Method Pattern) — это поведенческий паттерн проектирования, который определяет основу алгоритма в базовом классе, позволяя подклассам переопределять определенные шаги, не изменяя общую структуру.

Паттерн использует абстрактный класс с шаблонным методом, который вызывает последовательность шагов. Некоторые шаги имеют реализации по умолчанию, в то время как другие являются абстрактными и должны быть реализованы подклассами:

abstract class DataProcessor {
    // Шаблонный метод — определяет структуру алгоритма
    public final void process() {
        readData();
        processData();
        saveData();
    }
    
    abstract void readData();
    abstract void processData();
    
    // Реализация по умолчанию (метод-хук)
    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");
    }
}

Шаблонный метод помечен как final, чтобы предотвратить изменение структуры алгоритма подклассами. При вызове он выполняет одну и ту же последовательность для всех подклассов:

DataProcessor csv = new CSVProcessor();
csv.process();
// Output:
// Reading CSV file
// Parsing CSV data
// Saving to default location

Основные характеристики:

  • Шаблонный метод является final и определяет структуру алгоритма
  • Абстрактные методы должны быть реализованы подклассами
  • Методы-хуки (hooks) предоставляют реализации по умолчанию, которые могут быть опционально переопределены
  • Способствует повторному использованию кода путем размещения общей логики в базовом классе

Попробуйте сами

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Проверьте себя

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

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