Metoda fabrykująca to wzorzec projektowy, który pozwala na tworzenie w zorganizowany sposób różnych obiektów
implementujących ten sam interfejs.
Tak więc to nie my bezpośrednio tworzymy obiekt za pomocą słowa kluczowego
new,
tylko zgłaszamy się do fabryki z żądaniem wyprodukowania go zgodnie z naszymi oczekiwaniami.
Fabryka musi dostarczyć nam metodę wytwarzającą (fabrykującą) obiekt, ale to,
w jaki sposób go konstruuje, pozostaje dla nas niewidoczne.
Załóżmy, że tworzymy aplikację przechowującą zestawy dokumentów (dokument, załącznik, notatka).
Interesuje nas wygenerowanie szablonu dla każdego rodzaju dokumentu.
Dzięki niemu użytkownik dowie się, w jaki sposób ma wprowadzać do niego dane (co powinien wpisać w konkretnych polach).
Uruchomienie fabryki dokumentów
Nie mamy jeszcze żadnej klasy, ale już teraz warto zobaczyć, do czego będziemy dążyć w implementacji.
Funkcjonalność całej fabryki opierać się będzie na zestawie kilku klas, z których większość będzie niewidoczna
w miejscu wywołania. Tak naprawdę interesują nas tutaj tylko dostęp do klas odpowiedzialnych za produkcję
różnych rodzajów dokumentów (dokument podstawowy, załącznik, notatka).
Każda z tych klas (producentów) posiada metodę
createItem, której wywołanie spowoduje utworzenie
obiektu typu
Item. Oczywiście każda z klas dostarczy obiekt zbudowany zgodnie z jej
oryginalny projektem. I tak na przykład
DocumentItemProducer stworzy obiekt
DocumentItem,
a
AttachmentItemProducer zbuduje obiekt
AttachmentItem.
Kod do skopiowania, który...nie skompiluje się na tym etapie:
Kod nie skompiluje się, ponieważ najpierw musimy zbudować klasy, których tutaj używamy.
Produkcja
Wszystkie klasy produkujące nasze obiekty implementują interfejs (może to być też klasa abstrakcyjna).
Najlepiej nazwać go tak, aby kojarzył się z fabryką oraz z tym, jakie elementy ta fabryka wytwarza.
Stąd nadaliśmy mu nazwę
ItemFactory (fabryka tworząca różne rodzaje itemów).
Mamy trzy klasy, które implementują interfejs
ItemFactory. Każda z nich robi
to po swojemu. Producent dokumentów tworzy obiekt
DocumentItem, producent załączników
buduje obiekt
AttachmentItem, a producent notatek tworzy
NoteItem.
Ważne jest to, że każda implementacja metody konstruującej (
createItem) zwraca obiekt typu
Item, więc to mówi nam od razu, że
wszystkie trzy obiekty muszą implementować ten sam interfejs lub rozszerzać tę samą klasę.
Poza tworzeniem obiektów w ramach metody
createItem jest wywoływana jeszcze metoda
fillTemplates,
ale nie będziemy się nią teraz jakoś specjalnie zajmować. Na tym etapie wystarczy wiedzieć, że wypełnia ona każdy z obiektów dodatkowymi danymi.
Metoda fabrykująca
Dochodzimy do samego sedna, czyli do interfejsu fabryki
ItemFactory.
W nim znajduje się deklaracja metody
createItem,
która jest implementowana przez wszystkie klasy dziedziczące (przez wszystkich producentów).
Mamy więc jedną metodę fabryki, która
fabrykuje (wytwarza) na różne sposoby obiekty tego samego interfejsu
Item.
Mówimy, że wytwarzanie odbywa się na różne sposoby, ponieważ każda klasa dostarcza własną, niezależną implementację interfejsu
Item
(
DocumentItem,
AttachmentItem,
NoteItem).
Tak właśnie działa
metoda fabrykująca!
Produkty
Cały czas mówimy o tym, że klasy producentów tworzą obiekty ("produkty") klas rozszerzających abstrakcję
Item.
Zobaczmy więc w końcu, jak wyglądają klasy owych produktów:
W najprostszej wersji mogą to być puste klasy dziedziczące z pustej klasy abstrakcyjnej
Item, jednak my chcemy, aby
nasze klasy miały funkcjonalność. Chcemy wykonać część zasadniczą naszego zadania, a więc wykorzystać wzorzec
Metoda fabrykująca
do stworzenia wartościowych szablonów. Z tego powodu każda z metod przygotowuje nazwę pola dokumentu wraz z opisem informującym użytkownika, co powinno się znaleźć w
tym miejscu (co powinien wpisać lub dodać, na przykład w polu
content w ramach notatki powinien wpisać swoją...
notatkę). Może tego tutaj nie widać, ale za to dosyć łatwo można się domyślić, że dane wrzucamy do mapy (
templates),
inicjowanej w klasie
Item.
Tworzenie szablonów z użyciem wzorca
W poprzednim paragrafie przedstawiliśmy klasy implementujące abstrakcję
Item.
W metodzie
fillTemplates wypełniamy tam mapę przechowującą nazwę pola dokumentu wraz z szablonem.
Teraz pozostało nam zajrzeć do części abstrakcyjnej, z której korzystają wszystkie klasy (klasa
Item).
Tworzymy tutaj mapę szablonów. Kluczem w mapie będzie nazwa pola w dokumencie (załączniku lub notatce),
a wartością predefiniowany tekst oznajmiający użytkownikowi, co ma wpisać w danym polu.
Zakładamy, że każda forma dokumentu będzie wymagała miejsca na podpis oraz na datę podpisania dokumentu,
dlatego w konstruktorze uruchamianym przez każdą z klas dziedziczących wkładamy do mapy (za pomocą metody
initTemplates) kolejno "whoCreated" i "whenCreated".
Niezależnie od tego, czy stworzymy obiekt klasy
DocumentItem,
AttachmentItem, czy inny, każdy
z obiektów będzie miał wypełnione w mapie te wartości.
Pozostałe szablony są już specyficzne dla danego rodzaju dokumentu, dlatego są one dodawane z poziomu dedykowanych klas.
Wykorzystujemy do tego celu metodę
fillTemplates, którą nadpisujemy w klasach dziedziczących,
na przykład w klasie
DocumentItem:
To tyle, jeśli chodzi o strukturę klas tworzonych przez fabrykę.
Przypomnijmy sobie teraz, jak uruchamiamy metodę wypełniającą szablony.
Dzieje się to zaraz po tym, jak fabryka
stworzy obiekt dokumentu.
W momencie wywołania konstruktora uruchomiony jest kod konstruktora nadklasy
Item,
gdzie metoda
initTemplates wrzuca do mapy szablon do wprowadzenia autora oraz daty tworzenia dokumentu.
W kolejnej linii uruchamiamy metodę
fillTemplates,
która wypełnia mapę danymi specyficznymi dla dokumentu podstawowego.
Analogicznie działa to dla załącznika i notatki.
Uruchomienie
Podsumowując mamy fabrykę do produkcji różnych rodzajów dokumentów. Mamy też system przechowywania szablonów zbudowany na grupie klas implementujących interfejs
Item.
Teraz wystarczy to wszystko uruchomić. Co ważne, sama fabryka to...tylko fabryka i nie ma nic wspólnego z szablonami. Szablony dodaliśmy, aby pokazać przykład jej wykorzystania.
Wynik wykonania kodu:
Gdzie używamy wzorca Metoda fabrykująca ?
Wzorca używamy wszędzie tam, gdzie przewidujemy potrzebę tworzenia wielu różnych obiektów jednego interfejsu.
Takie rozwiązanie odseparuje nam główny kod biznesowy naszego programu od obszaru, w którym tylko produkujemy obiekty.
W ten sposób w przyszłości łatwiej będzie dołożyć nowe klasy implementujące wspomniany interfejs,
a z drugiej strony taka rozbudowa klas nie skomplikuje zasadniczej części kodu. Takie odseparowanie kodu nazywamy hermetyzacją.
Fabryka, poprzez to, że produkuje obiekty konkretnego interfejsu, skupia się na produkcji konkretnej grupy obiektów.
Biorąc pod uwagę to, że praktycznie w każdej aplikacji mamy do czynienia z takimi grupami, dosyć łatwo można przytoczyć przypadki użycia.
I tak na przykład budując system do rezerwacji biletów na wydarzenia, możemy zaimplementować fabrykę
EventFactory,
produkującą obiekty klasy
Event. Wtedy będzie ona tworzyć szczegółowe implementacje pod postaciami
MusicEvent,
SportEvent,
ScienficEvent czy
MotivationalEvent.
W takiej sytuacji będziemy mogli stworzyć abstrakcyjną klasę
Event i wypełnić ją danymi takimi jak
nazwa, czy data rozpoczęcia i zakończenia eventu (cecha wspólna wszystkich eventów). Natomiast klasy dziedziczące będą zawsze dokładać "coś od siebie".
Na przykład w ramach wydarzenia muzycznego może to być rodzaj granej muzyki, a w ramach eventu naukowego definicja eksperymentu.
Autor: Jarek Klimas
Data: 03 stycznia 2024
Labele: Backend, Podstawowy, Java
Czy informacje, które otrzymałeś, były pomocne?
Jeśli tak, zapraszam Cię do podarowania mi kawy.
Jeśli interesują Cię wzorce projektowe, możesz być również zainteresowany naszym Kursem Aplikacji Web,
w którym uczysz się kompleksowo Springa, Hibernate'a i innych aktualnych rozwiązań technologicznych
na bazie
gotowej aplikacji webowej.
Oto co dokładnie znajdziesz w kursie:
-
Kurs implementacji aplikacji webowej zbudowanej za pomocą:
- Spring Boot 2, Spring Framework 5, Spring Data JPA, Spring MVC
- Spring Security, Spring AOP
- Hibernate
- REST Api, Format JSON
- Maven
Aplikacja obejmuje funkcjonalności od rejestracji użytkownika i logowania, przez tworzenie, edycję oraz usuwanie danych,
aż po różne formy prezentacji, w tym tabele i wykresy. Kurs obejmuje również implementację resetowania hasła
z linkiem potwierdzającym wysyłanym na adres email.
-
Atak CSRF i jak się przed nim bronić w Springu
-
Clickjacking za pomocą iframe - jak się zabezpieczyć za pomocą Spring Security
-
CORS i jego możliwe opcje w Springu
-
Testowanie CORS-a z użyciem cURL
-
Dodatkowy projekt do nauki samego Hibernate'a, przygotowany w dwóch wersjach (z Lombokiem i bez).
Ta część bazy wiedzy zawiera ponad 60 omówionych snippetów kodu, wzbogaconych dodatkowo ponad 40 zdjęciami.
W projekcie zostały zaimplementowane i omówione między innymi:
- Podstawy Hibernate'a (adnotacje, encje itp.)
- Wszystkie rodzaje relacji bazodanowych
- Orphan Removal
- Single Table Discriminator
- Table Per Class
...oraz wiele innych zagadnień.
Sam kurs implementacji aplikacji to ponad 150 stron (całość online) analizy kodu od frontendu przez REST-owe wysyłanie żądań i odbieranie odpowiedzi, po zapis w bazie i wizualizowanie zapisu we frontendzie.
Dokładnie rozpisane ścieżki w kodzie z tabelami kolejnych kroków dla poszczególnych funkcjonalności. Drogowskazy do ogromnej bazy wiedzy
dołączonej w postaci kursów Spring i Hibernate (z projektami wspierającymi naukę).
Zobacz aplikację