Template Method Pattern – Przewodnik dla Programistów Java

TL;DR: Template Method to wzorzec projektowy, który definiuje szkielet algorytmu w klasie bazowej, pozwalając podklasom zmieniać konkretne kroki bez naruszania struktury całego procesu. Idealny do eliminowania duplikacji kodu i zapewnienia spójności.

Wyobraź sobie, że parzysz kawę i herbatę. Oba procesy są podobne: gotowanie wody, dodanie składników, zalanie gorącą wodą, dodanie dodatków. Różnią się tylko szczegóły – jaki składnik dodajesz i jakie dodatki. Template Method pattern działa dokładnie tak samo – definiuje ogólny przepis, a konkretne implementacje wypełniają szczegóły.

Dlaczego to ważne: Template Method eliminuje duplikację kodu w sytuacjach, gdzie mamy podobne procesy różniące się tylko szczegółami. Jest szeroko używany w Spring Framework, szczególnie w klasach abstrakcyjnych jak AbstractController czy JdbcTemplate.

Co się nauczysz:

  • Jak działa wzorzec Template Method i kiedy go stosować
  • Implementacja pattern w Javie z praktycznymi przykładami
  • Zastosowania w Spring Framework i bibliotekach Java
  • Różnice między Template Method a Strategy pattern
  • Najlepsze praktyki i typowe pułapki
Wymagania wstępne: Podstawowa znajomość Javy, dziedziczenia, klas abstrakcyjnych i metod. Pomocna będzie znajomość podstaw programowania obiektowego.

Czym jest Template Method Pattern?

Template Method to wzorzec behawioralny, który definiuje szkielet algorytmu w metodzie klasy abstrakcyjnej, odkładając implementację niektórych kroków do podklas. Pozwala to podklasom przedefiniować pewne kroki algorytmu bez zmieniania jego struktury.

Template Method Pattern – wzorzec projektowy definiujący ogólny algorytm w klasie bazowej, gdzie konkretne kroki są implementowane w podklasach.

Kluczowe elementy wzorca

  • Abstract Class: Definiuje template method i abstrakcyjne kroki
  • Template Method: Metoda definiująca szkielet algorytmu
  • Primitive Operations: Abstrakcyjne metody implementowane w podklasach
  • Concrete Classes: Implementują konkretne kroki algorytmu

Implementacja w Javie

Zobaczmy praktyczny przykład systemu generowania raportów, gdzie różne typy raportów mają podobną strukturę ale różne sposoby zbierania i formatowania danych:

// Klasa abstrakcyjna definiująca template method
public abstract class ReportGenerator {
    
    // Template method - definiuje szkielet algorytmu
    public final String generateReport() {
        String header = createHeader();
        String data = collectData();
        String formattedData = formatData(data);
        String footer = createFooter();
        
        return header + formattedData + footer;
    }
    
    // Metody abstrakcyjne - implementowane w podklasach
    protected abstract String collectData();
    protected abstract String formatData(String data);
    
    // Metody z domyślną implementacją - można override'ować
    protected String createHeader() {
        return "=== RAPORT ===\n";
    }
    
    protected String createFooter() {
        return "\n=== KONIEC ===" ;
    }
}

Teraz implementujemy konkretne typy raportów:

// Raport sprzedażowy
public class SalesReportGenerator extends ReportGenerator {
    
    @Override
    protected String collectData() {
        // Symulacja zbierania danych sprzedażowych
        return "Sprzedaż Q4: 150000 PLN\nNowi klienci: 25";
    }
    
    @Override
    protected String formatData(String data) {
        return "--- DANE SPRZEDAŻOWE ---\n" + data + "\n";
    }
    
    @Override
    protected String createHeader() {
        return "=== RAPORT SPRZEDAŻOWY ===\n";
    }
}

// Raport finansowy  
public class FinancialReportGenerator extends ReportGenerator {
    
    @Override
    protected String collectData() {
        return "Przychody: 500000 PLN\nKoszty: 350000 PLN\nZysk: 150000 PLN";
    }
    
    @Override
    protected String formatData(String data) {
        return "$$$ FINANSE $$$\n" + data + "\n$$$$$$$$$$$$$$";
    }
}
Pro tip: Zwróć uwagę na słowo kluczowe final w template method. Zapobiega to przypadkowemu override’owaniu głównej logiki w podklasach.

Użycie w praktyce

public class ReportService {
    public static void main(String[] args) {
        // Generowanie różnych typów raportów
        ReportGenerator salesReport = new SalesReportGenerator();
        ReportGenerator financialReport = new FinancialReportGenerator();
        
        System.out.println(salesReport.generateReport());
        System.out.println("\n" + financialReport.generateReport());
    }
}

Template Method w Spring Framework

Spring Framework intensywnie używa Template Method pattern. Najlepszym przykładem jest JdbcTemplate:

// Spring's JdbcTemplate używa Template Method
@Service
public class UserService {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public List findAllUsers() {
        return jdbcTemplate.query(
            "SELECT * FROM users",
            (rs, rowNum) -> new User(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getString("email")
            )
        );
    }
}
Analogia: JdbcTemplate to jak ekspres do kawy – ma zdefiniowany proces (nawiązanie połączenia, wykonanie query, zamknięcie połączenia), a ty podajesz tylko specificzne instrukcje (SQL i sposób mapowania wyników).

Template Method vs Strategy Pattern

Template MethodStrategy
Używa dziedziczeniaUżywa kompozycji
Szkielet w klasie bazowejCały algorytm w strategii
Statyczny wybór w compile-timeDynamiczny wybór w runtime
Jedna odpowiedzialność na klasęMożna łączyć różne strategie

Najlepsze praktyki

Uwaga: Nie używaj Template Method gdy algorytm może się często zmieniać – lepszy będzie Strategy pattern.
  • Template method jako final: Zapobiega przypadkowemu nadpisaniu logiki
  • Minimalna liczba kroków: Za dużo abstrakcyjnych metod = trudniejszy maintenance
  • Czytelne nazwy: Nazwy metod powinny jasno określać co robią
  • Dokumentacja: Opisz kontrakt każdej abstrakcyjnej metody
Typowy błąd: Tworzenie zbyt skomplikowanych hierarchii z wieloma poziomami Template Method. Lepiej ograniczyć się do 2-3 poziomów.

Kiedy używać Template Method?

Template Method sprawdza się idealnie gdy:

  • Masz podobne algorytmy różniące się tylko szczegółami
  • Chcesz wymusić określoną kolejność kroków
  • Niektóre kroki są opcjonalne (hook methods)
  • Potrzebujesz kontroli nad całym procesem
Pułapka: Template Method tworzy silne powiązanie między klasą bazową a podklasami. Zmiana w template method wpływa na wszystkie implementacje.
Czy mogę mieć więcej niż jedną template method w klasie?

Tak, możesz mieć kilka template methods w jednej klasie abstrakcyjnej. Każda może definiować inny proces lub workflow.

Jakie są alternatywy dla Template Method?

Strategy pattern (kompozycja zamiast dziedziczenia), Command pattern (enkapsulacja operacji) lub proste funkcje callback w języku z first-class functions.

Czy Template Method łamie Open/Closed Principle?

Nie, wręcz przeciwnie – klasa bazowa jest zamknięta na modyfikacje ale otwarta na rozszerzenia poprzez dziedziczenie.

Jak testować klasy używające Template Method?

Testuj konkretne implementacje, nie klasę abstrakcyjną. Możesz też stworzyć test doubles dziedziczące z klasy bazowej.

Czy Template Method działa z interfejsami?

W Javie 8+ możesz używać default methods w interfejsach do implementacji Template Method, ale klasy abstrakcyjne są bardziej naturalne.

Przydatne zasoby:

🚀 Zadanie dla Ciebie

Stwórz system walidacji formularzy używając Template Method pattern. Klasa bazowa powinna definiować proces: sprawdzenie wymaganych pól → walidacja formatu → walidacja biznesowa → zwrócenie wyniku. Zaimplementuj co najmniej dwie konkretne walidacje: UserRegistrationValidator i ContactFormValidator.

Masz doświadczenie z Template Method pattern? Podziel się w komentarzach swoimi przemyśleniami – gdzie stosowałeś ten wzorzec i jakie napotkałeś wyzwania!

Zostaw komentarz

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

Przewijanie do góry