Metoda szablonowa (ang. template method) jest wzorcem projektowym, który pozwala na zdefiniowanie szkieletu zachowania lub algorytmu w klasie abstrakcyjnej i jego uszczegółowienie w klasach pochodnych. Template method jest rodzajem czynnościowego wzorca projektowego. Więcej o typach wzorców projektowych można poczytać tutaj [under construction].
Przykładem mogłaby być klasa generująca raporty. Raporty mogą być różne - finansowe, zawierające dane o ilości sprzedanych produktów albo o ilości jajek zniesionych przez kury na farmie. Jednak posiadają one pewną cechę wspólną - dla każdego raportu chcemy mieć nagłówek mówiący czego on dotyczy, jego ciało zawierające główną treść, i stopkę z informacją o dacie wygenerowania.
Nie chcemy żeby każda z klas FinancialReportPrinter, SalesReportPrinter i HensWorkReportPrinter zawierała w sobie logikę mówiącą, że na górze raportu jest header, potem body, a potem footer. Byłoby to zbędne powtarzanie. W razie potrzeby dodania podsumowania między ciałem, a stopką z datą, trzeba by wprowadzić zmiany w każdej z tych klas, co byłoby nie tylko denerwujące, lecz także narażałoby kod na błędy.
Dlatego lepiej jest zastosować wzorzec template method i zdefiniować abstrakcyjną klasę ReportPrinter, w której zawrze się ogólna logika generowania raportu. Dziedziczyć z niej będą klasy FinancialReportPrinter, SalesReportPrinter i HensWorkReportPrinter i każda z nich doprecyzuje na czym właściwie będzie polegać drukowanie poszczególnych sekcji.
W razie potrzeby dodania do wszystkich raportów nowej sekcji, logikę zmienimy tylko w klasie bazowej. W klasach pochodnych pozostanie nam tylko dopisanie implementacji nowej metody drukującej dodaną sekcję.
public abstract class ReportPrinter { public void Print() { PrintHeader(); PrintBody(); PrintFooter(); } protected abstract void PrintHeader(); protected abstract void PrintBody(); protected abstract void PrintFooter(); } public class HensWorkReportPrinter : ReportPrinter { protected override void PrintHeader() { Console.WriteLine("Raport o zniesionych jajkach:"); } protected override void PrintBody() { Console.WriteLine("Zniesiono 16 jajek."); } protected override void PrintFooter() { Console.WriteLine("Raport o jajkach wygenerowano w dniu " + DateTime.Now.ToShortDateString()); } } public class SalesWorkReportPrinter : ReportPrinter { protected override void PrintHeader() { Console.WriteLine("Raport o sprzedaży:"); } protected override void PrintBody() { Console.WriteLine("Sprzedano czterech ziomków na psach."); } protected override void PrintFooter() { Console.WriteLine("Raport o sprzedaży wygenerowano w dniu " + DateTime.Now.ToShortDateString()); } }
Innym przykładem zastosowanie tego wzorca mogą być frameworki do pisania unit testów. W większości z nich kroki wykonywania testów są podobne - najpierw wołamy metodę która przygotowuje środowisko testowania, potem wywołuje się ciało testu, a na koniec dokonujemy weryfikacji czy test przeszedł, czy nie.
Podobnie sprawa ma się z grami planszowymi, w których rozgrywka zawsze zaczyna się od rozłożenia planszy, potem od właściwej gry, a kończy się podliczeniem punktów i wyłonieniem zwycięzcy. Przykład ten omawiam w tym poście.
Kod z tego posta, wraz z innymi przykładami wzorców projektowych, można pobrać z tego repozytorium [under construction]
Komentarze
Prześlij komentarz