Template Method Pattern – Krok po Kroku dla Początkujących

TL;DR: Template Method to wzorzec projektowy który pozwala zdefiniować szkielet algorytmu w klasie bazowej, a szczegóły implementacji zostawić klasom pochodnym. To jak przepis na ciasto – podstawowe kroki są stałe, ale możesz zmienić składniki według własnego gustu.

Dlaczego Template Method Pattern jest ważny?

W programowaniu często spotykamy sytuacje gdzie różne klasy wykonują podobne operacje, ale z drobnymi różnicami. Template Method pattern rozwiązuje ten problem eliminując duplikację kodu i zapewniając spójną strukturę procesów biznesowych. To fundamentalny wzorzec używany w Spring Framework, Apache Commons i wielu innych bibliotekach Java.

Co się nauczysz:

  • Czym jest Template Method pattern i kiedy go używać
  • Jak implementować wzorzec w Javie 8 krok po kroku
  • Praktyczne przykłady z życia wzięte
  • Najczęstsze błędy początkujących i jak ich unikać
  • Różnice między Template Method a Strategy pattern

Wymagania wstępne:

  • Podstawowa znajomość Javy (klasy, dziedziczenie, metody abstrakcyjne)
  • Rozumienie pojęć: polimorfizm, hermetyzacja
  • 6-12 miesięcy doświadczenia z programowaniem obiektowym

Czym jest Template Method Pattern?

Template Method to behawioralny wzorzec projektowy który definiuje szkielet algorytmu w metodzie klasy bazowej, delegując niektóre kroki do klas pochodnych. Klasy pochodne mogą przedefiniować poszczególne kroki algorytmu bez zmiany jego ogólnej struktury.

Analogia: Template Method to jak przepis na pizzę. Podstawowe kroki są zawsze takie same: przygotuj ciasto, dodaj sos, dodaj składniki, wrzuć do pieca. Ale każda pizzeria może mieć swoje unikalne składniki i sposób ich przygotowania.

Praktyczny przykład – System raportowania

Wyobraź sobie że musisz stworzyć system generujący różne typy raportów: PDF i Excel. Oba mają podobną strukturę, ale różnią się formatowaniem.

// Klasa abstrakcyjna definiująca template method
public abstract class ReportGenerator {
    
    // Template method - szkielet algorytmu
    public final void generateReport() {
        collectData();
        formatHeader();
        formatData();
        formatFooter();
        saveReport();
    }
    
    // Metody wspólne dla wszystkich raportów
    protected void collectData() {
        System.out.println("Zbieranie danych z bazy.....");
    }
    
    protected void saveReport() {
        System.out.println("Zapisywanie raportu na dysk...");
    }
    
    // Metody abstrakcyjne - do implementacji w klasach pochodnych
    protected abstract void formatHeader();
    protected abstract void formatData();
    protected abstract void formatFooter();
}
// Konkretna implementacja dla PDF
public class PDFReportGenerator extends ReportGenerator {
    
    @Override
    protected void formatHeader() {
        System.out.println("Formatowanie nagłówka PDF z logo...");
    }
    
    @Override
    protected void formatData() {
        System.out.println("Formatowanie danych do tabeli PDF...");
    }
    
    @Override
    protected void formatFooter() {
        System.out.println("Dodawanie stopki z numeracją stron PDF...");
    }
}

// Konkretna implementacja dla Excel
public class ExcelReportGenerator extends ReportGenerator {
    
    @Override
    protected void formatHeader() {
        System.out.println("Tworzenie nagłówka w pierwszym wierszu Excel...");
    }
    
    @Override
    protected void formatData() {
        System.out.println("Wypełnianie komórek danymi Excel...");
    }
    
    @Override
    protected void formatFooter() {
        System.out.println("Dodawanie formuł sumujących Excel...");
    }
}

Użycie wzorca w praktyce

public class ReportingService {
    
    public static void main(String[] args) {
        
        // Generowanie raportu PDF
        ReportGenerator pdfReport = new PDFReportGenerator();
        pdfReport.generateReport();
        
        System.out.println("---");
        
        // Generowanie raportu Excel  
        ReportGenerator excelReport = new ExcelReportGenerator();
        excelReport.generateReport();
    }
}
Rezultat: Oba raporty przechodzą przez te same kroki, ale każdy formatuje dane na swój sposób. Jeśli chcesz dodać nowy typ raportu (np. CSV), wystarczy utworzyć nową klasę dziedziczącą po ReportGenerator.

Kluczowe cechy wzorca

Template Method – metoda w klasie bazowej która definiuje szkielet algorytmu. Zazwyczaj oznaczona jako final żeby nie można było jej nadpisać.
Hook Methods – opcjonalne metody które klasy pochodne mogą nadpisać, ale nie muszą. Mają domyślną (często pustą) implementację.
public abstract class DataProcessor {
    
    public final void processData() {
        loadData();
        if (shouldValidate()) {  // Hook method
            validateData();
        }
        transformData();
        saveData();
    }
    
    // Hook method z domyślną implementacją
    protected boolean shouldValidate() {
        return true;  // Domyślnie zawsze waliduj
    }
    
    protected abstract void loadData();
    protected abstract void transformData();
    protected abstract void saveData();
    
    private void validateData() {
        System.out.println("Walidacja danych...");
    }
}

Template Method vs Strategy Pattern

Template MethodStrategy Pattern
Używa dziedziczeniaUżywa kompozycji
Definiuje szkielet algorytmuEnkapsuluje całe algorytmy
Klasy pochodne nadpisują częściObiekty strategy są wymienne
Struktura algorytmu stałaCały algorytm może się zmienić
Częsty błąd początkujących: Zapominanie o oznaczeniu template method jako final. Bez tego klasy pochodne mogą nadpisać cały algorytm, co łamie idę wzorca.

Wzorzec w praktyce – Spring Framework

Spring Framework intensywnie używa Template Method pattern. Przykład to JdbcTemplate który definiuje szablon dla operacji bazodanowych:

// Uproszczony przykład inspirowany Spring JDBC
public abstract class JdbcOperations {
    
    public final List executeQuery(String sql) {
        Connection conn = getConnection();
        PreparedStatement stmt = prepareStatement(conn, sql);
        ResultSet rs = stmt.executeQuery();
        List results = mapResults(rs);  // Abstract method
        cleanup(conn, stmt, rs);
        return results;
    }
    
    protected abstract List mapResults(ResultSet rs);
}
Kiedy używać Template Method pattern?

Używaj gdy masz kilka klas wykonujących podobne operacje z drobnymi różnicami. Wzorzec eliminuje duplikację kodu i zapewnia spójną strukturę procesów.

Czy template method musi być final?

Tak, powinien być final żeby klasy pochodne nie mogły zmienić struktury algorytmu. Mogą tylko nadpisywać konkretne kroki.

Co to są hook methods?

To opcjonalne metody które klasy pochodne mogą nadpisać ale nie muszą. Mają domyślną implementację (często pustą) w klasie bazowej.

Jak Template Method różni się od Strategy?

Template Method używa dziedziczenia i definiuje szkielet algorytmu. Strategy używa kompozycji i enkapsuluje całe algorytmy które są wymienne.

Czy mogę mieć kilka template methods w jednej klasie?

Tak, klasa może mieć kilka template methods dla różnych procesów. Każdy definiuje swój własny szkielet algorytmu.

Przydatne zasoby:

🚀 Zadanie dla Ciebie

Stwórz system przetwarzania plików używając Template Method pattern. Klasa bazowa powinna definiować kroki: otwórz plik, przetwórz zawartość, zapisz wynik, zamknij plik. Stwórz dwie implementacje: jedną dla plików tekstowych (zmiana na wielkie litery) i drugą dla plików CSV (dodanie numeru linii). Przetestuj oba warianty.

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry