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.
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
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.
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$$$$$$$$$$$$$$"; } }
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 ListfindAllUsers() { return jdbcTemplate.query( "SELECT * FROM users", (rs, rowNum) -> new User( rs.getLong("id"), rs.getString("name"), rs.getString("email") ) ); } }
Template Method vs Strategy Pattern
Template Method | Strategy |
---|---|
Używa dziedziczenia | Używa kompozycji |
Szkielet w klasie bazowej | Cały algorytm w strategii |
Statyczny wybór w compile-time | Dynamiczny wybór w runtime |
Jedna odpowiedzialność na klasę | Można łączyć różne strategie |
Najlepsze praktyki
- 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
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
Tak, możesz mieć kilka template methods w jednej klasie abstrakcyjnej. Każda może definiować inny proces lub workflow.
Strategy pattern (kompozycja zamiast dziedziczenia), Command pattern (enkapsulacja operacji) lub proste funkcje callback w języku z first-class functions.
Nie, wręcz przeciwnie – klasa bazowa jest zamknięta na modyfikacje ale otwarta na rozszerzenia poprzez dziedziczenie.
Testuj konkretne implementacje, nie klasę abstrakcyjną. Możesz też stworzyć test doubles dziedziczące z klasy bazowej.
W Javie 8+ możesz używać default methods w interfejsach do implementacji Template Method, ale klasy abstrakcyjne są bardziej naturalne.
Przydatne zasoby:
- Java 8 API Documentation
- Spring Framework Reference
- Template Method na Refactoring Guru
- Baeldung – Template Method Pattern
🚀 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!