Wzorzec metody szablonowej
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 97 z 110.
Wzorzec Template Method definiuje szkielet algorytmu w klasie bazowej, umożliwiając podklasom nadpisywanie określonych kroków bez zmiany ogólnej struktury. Klasa bazowa kontroluje przepływ pracy, a podklasy dostosowują poszczególne kroki.
Ten wzorzec wykorzystuje dziedziczenie: klasa abstrakcyjna definiuje metodę szablonową, która wywołuje sekwencję kroków, z których niektóre są abstrakcyjne (muszą zostać zaimplementowane przez podklasy), a niektóre mają domyślne implementacje:
abstract class DataProcessor {
// Metoda szablonowa – określa strukturę algorytmu
void process() {
readData();
processData();
saveData();
}
void readData(); // Abstrakcyjna – podklasy muszą ją zaimplementować
void processData(); // Abstrakcyjna – podklasy muszą ją zaimplementować
void saveData() { // Domyślna implementacja
print('Data saved to database');
}
}
class CsvProcessor extends DataProcessor {
@override
void readData() => print('Reading CSV file');
@override
void processData() => print('Parsing CSV rows');
}
class JsonProcessor extends DataProcessor {
@override
void readData() => print('Reading JSON file');
@override
void processData() => print('Parsing JSON objects');
@override
void saveData() => print('Saved to cloud storage');
}
void main() {
var csv = CsvProcessor();
csv.process();
// Odczytywanie pliku CSV
// Przetwarzanie wierszy CSV
// Dane zapisano w bazie danych
var json = JsonProcessor();
json.process();
// Odczytywanie pliku JSON
// Przetwarzanie obiektów JSON
// Zapisano w chmurze
}Metoda process() jest szablonem — określa kolejność operacji. Podklasy implementują readData() i processData() w różny sposób, a opcjonalnie mogą przesłonić saveData(). Główną zaletą jest to, że struktura algorytmu pozostaje spójna we wszystkich implementacjach, a poszczególne kroki można dostosowywać.
Wyzwanie
ŁatwyZbudujmy system generowania raportów z użyciem wzorca metody szablonowej! Stworzysz szkielet, w którym różne typy raportów mają tę samą ogólną strukturę, ale każdy typ raportu dostosowuje określone kroki, takie jak pobieranie danych i formatowanie wyniku.
Podzielisz kod na dwa pliki:
report_generator.dart: Ten plik zawiera abstrakcyjną klasę bazową oraz konkretne implementacje raportów. Utwórz klasę abstrakcyjnąReportGeneratorz metodą szablonową o nazwiegenerate(), która definiuje proces generowania raportu w następującej kolejności:fetchData(),analyzeData(),formatReport()ideliver(). MetodyfetchData()iformatReport()powinny być abstrakcyjne (każdy typ raportu obsługuje je inaczej), natomiastanalyzeData()powinna wypisywaćAnalyzing data..., adeliver()powinna wypisywaćReport delivered via email.jako domyślne implementacje. Następnie utwórz dwie konkretne klasy raportów:SalesReport— jej metodafetchData()wypisujeFetching sales data from database..., a metodaformatReport()wypisujeFormatting as bar charts and tables.InventoryReport— jej metodafetchData()wypisujeFetching inventory levels from warehouse..., metodaformatReport()wypisujeFormatting as inventory list., a metodadeliver()jest nadpisana tak, aby wypisywaćReport sent to warehouse manager.
main.dart: Zaimportuj plik generatora raportów i pokaż, jak wzorzec metody szablonowej pozwala zachować spójną strukturę algorytmu, a jednocześnie umożliwia jej dostosowanie. Utwórz obiektSalesReporti wywołaj jego metodęgenerate(). Następnie wypisz pusty wiersz, aby oddzielić wyniki. Utwórz obiektInventoryReporti również wywołaj jego metodęgenerate().
Zwróć uwagę, że oba raporty wykonują ten sam czteroetapowy proces, ale każdy dostosowuje kroki istotne dla swojego konkretnego celu. Klasa InventoryReport nadpisuje nawet domyślną metodę dostarczania, aby wysyłać raporty do innego odbiorcy!
Oczekiwany wynik:
Fetching sales data from database...
Analyzing data...
Formatting as bar charts and tables.
Report delivered via email.
Fetching inventory levels from warehouse...
Analyzing data...
Formatting as inventory list.
Report sent to warehouse manager.Spróbuj swoich sił
import 'report_generator.dart';
void main() {
// TODO: Utwórz SalesReport i wywołaj jego metodę generate()
// TODO: Wypisz pustą linię, aby oddzielić wyniki
// TODO: Utwórz InventoryReport i wywołaj jego metodę generate()
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatności12Asynchroniczne OOP
Futures i async/awaitPodstawy strumieniKontrolery strumieniKonstruktory asynchroniczneAsynchroniczność w metodach klasyPodsumowanie — pobieranie danych15Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec repozytoriumPoćwicz samodzielnie: Kompilator Dart online