Kilka razy spotkałem się z opinią, że wzorce projektowe są pewnym uzupełnieniem języków programowania, ponieważ realizują zadania, które powinny być d
Kilka razy spotkałem się z opinią, że wzorce projektowe są pewnym uzupełnieniem języków programowania, ponieważ realizują zadania, które powinny być dostępne out of the box. Trudno polemizować z takim stwierdzeniem w przypadku singletona czy factory method, ale istnieje spora grupa wzorców projektowych, które trudno byłoby po prostu zaimplementować bezpośrednio w języku. 

  Do takiej grupy z pewnością należy zaklasyfikować template method, który po polsku nazywamy metodą szablonową. Template method zaliczamy do zespołu tzw. wzorców zachowań.   Idea wzorca Template method jest w moim odczuciu jednym z najprostszych wzorców projektowych — bardzo łatwo jest go zastosować, nawet nie zdając sobie z tego sprawy. Podczas tworzenia różnorakich fragmentów kodu często zdarza nam się dojść do sytuacji, w której uzyskujemy zbliżone do siebie klasy, różniące się pewnymi detalami implementacyjnymi. W takiej sytuacji przed oczami staje nam całe spektrum wzorców oraz rozwiązań. Bardzo łatwo można poczuć się przytłoczonym.   Przedstawiona powyżej sytuacja to duże pole do popisu dla template method. Wzorzec metody szablonowej wykorzystuje klasę abstrakcyjną do zaimplementowania lub utworzenia deklaracji wspólnych metod oraz swoistą metodę run, która w odpowiedniej kolejności wywołuje wszystkie metody pośrednie.   W ten sposób tworzymy łańcuch wywołań, w którym w poszczególnych implementacjach możemy wymieniać składniki. Przykład zaprezentowano na diagramie.     W klasie bazowej znajdują się metoda szablonowa oraz trzy metody cząstkowe. Klasy konkretne dziedziczą z klasy bazowej i jednocześnie implementują metodę nazwaną methodTwo, która różni się pomiędzy poszczególnymi implementacjami.   Przykład praktyczny Wyobraźmy sobie, że tworzymy aplikację mobilną, która do generowania layoutu wykorzystuje klasy. Przy dostępnych obecnie kontrolkach oraz wykorzystaniu szeroko rozumianego layoutu adaptacyjnego istnieje spora szansa na podzielenie kodu layoutu w taki sposób, by częściowo reużyć tych samych jego fragmentów w wersji potrait oraz landscape. Jednocześnie oba widoki będą się składały z takich samych elementów.   Poniżej kod klasy abstrakcyjnej — bazy dla obu layotuów: [sourcecode language="csharp"] public abstract class LayoutBase { protected virtual void RenderHeader() { Console.WriteLine("LayoutBase:RenderHeader()"); } protected abstract void RenderBody(); protected virtual void RenderFooter() { Console.WriteLine("LayoutBase:RenderFooter()"); } public void BuildLayout() { RenderHeader(); RenderBody(); RenderFooter(); } } [/sourcecode] Jak nietrudno zauważyć, metoda BuildLayout pełni funkcję metody szablonowej i wykorzystuje trzy metody podrzędne, z których dwie zostały już zdefiniowane, a trzecia powinna być zaimplementowana przez klasy konkretne. Warto również zwrócić uwagę na słowo kluczowe virtual. Zakładamy potencjalną zmienność dowolnego ze składników oraz stałość samej metody szablonowej.   Poniżej implementacja dwóch klas konkretnych — po jednej dla każdego z layoutów: [sourcecode language="csharp"] public class LayoutPortrait : LayoutBase { protected override void RenderBody() { Console.WriteLine("LayoutPortrait:RenderBody()"); } } public class LayoutLandscape : LayoutBase { protected override void RenderBody() { Console.WriteLine("LayoutLandscape:RenderBody()"); } } [/sourcecode] W obu przypadkach zaimplementowaliśmy abstrakcyjną metodę RenderBody, ale nic nie stało na przeszkodzie, by np. nadpisać również jeden ze składników. Wzorzec template method daje na tym polu sporą elastyczność.   Zwieńczeniem przykładu jest testowa klasa Program: [sourcecode language="csharp"] public class Program { public static void Main(string[] args) { LayoutPortrait portrait = new LayoutPortrait(); portrait.BuildLayout(); LayoutLandscape landscape = new LayoutLandscape(); landscape.BuildLayout(); Console.Read(); } } [/sourcecode] Wynik jej działania w konsoli powinien być zgodny z oczekiwanym: LayoutBase:RenderHeader() LayoutPortrait:RenderBody() LayoutBase:RenderFooter() LayoutBase:RenderHeader() LayoutLandscape:RenderBody() LayoutBase:RenderFooter()   Podsumowanie Wzorca template method — jak wszystkiego w życiu — warto używać z rozwagą. Zasadniczo jest on prostą receptą na rozwiązanie wielu problemów powtarzającego się kodu. Oczywiście należy znaleźć rozsądną granicę. Jeśli kod osiągnie kilkaset linii, a metoda szablonowa będzie się składać z 10 metod cząstkowych, to powinien być dla nas sygnał, że jednak coś tu nie do końca zagrało.  

Jerzy Piechowiak

Altcontroldelete.pl