Kurs Java

Wzorce projektowe

Tu jesteś
Java - Mapa kariery Tu jesteś 1 Java Wzorce projektowe Eclipse / IntelliJ Git 2 Maven 3 SQL
Relacyjne bazy danych niezmiennie od lat
są podstawą w budowie systemów
informatycznych. Na rynku istnieją
oczywiście również bazy typu NoSQL,
ale te zwykle są stosowane do
dedykowanych rozwiązań, jak na przykład
przechowywanie ogromnych ilości
danych w celu szybkiego przeszukiwania.
Wzorce projektowe najprościej rzecz ujmując są strażnikami ładu i porządku w kodzie źródłowym. Dzięki ich zastosowaniu kod staje się bardziej przejrzysty i uniwersalny oraz łatwiej modyfikować go w przyszłości. Programując w Javie powinniśmy czym prędzej nauczyć się odpowiednich wzorców, tak aby w trakcie pracy nie marnować czasu na wymyślanie koła od nowa oraz żeby nie nabierać przypadkowo błędnych przyzwyczajeń.

Podstawowe kategorie wzorców

Mamy ukształtowany podział wzorców na kategorie:
  • Wzorce kreacyjne
    Zadaniem tych wzorców jest tworzenie, inicjalizacja oraz konfiguracja obiektów, klas czy również innych typów danych.
    • opisują abstrakcyjne metody tworzenia obiektów
    • wskazują na uniezależnienie systemu od sposobu tworzenia obiektów
  • Wzorce strukturalne
    Wzorce te zajmują się powiązaniami i interakcją pomiędzy obiektami. Celem działania wzorca jest uporządkowanie struktury obiektów.
    • opisują sposób wiązania obiektów w struktury o określonych właściwościach
    • wskazują na właściwe wykorzystanie dziedziczenia i kompozycji
  • Wzorce czynnościowe/operacyjne (behawioralne)
    Ta grupa wzorców zajmuje się komunikacją pomiędzy obiektami. Stosowane są przede wszystkim w algorytmach oraz skomplikowanych systemach odpowiedzialności pomiędzy obiektami.
    • opisują algorytmy realizacji typowych zadań i przydział odpowiedzialności
    • wskazują na opis przepływu kontroli i interakcji

Przykłady wzorców

W przypadku tworzenia aplikacji z dowolnym interfejsem graficznym, a więc i w przypadku aplikacji webowych, należy jeszcze wspomnieć o dwóch niezwykle ważnych wzorcach...i tu uwaga... architektonicznych: MVC, MVVM. Nie są one co prawda wzorcami stricte projektowymi (tylko właśnie architektonicznymi), ale jednak bardzo mocno nawiązują do tej tematyki i uznaliśmy, że to właściwe miejsce do ich wykazania. Już wkrótce dodamy opisy dla przynajmniej części z wymienionych tutaj wzorców.
  • Wzorce architektoniczne
    • MVC
    • MVVM
  • Wzorce kreacyjne
    • Singleton
    • Fabryka (Factory)
    • Fabryka abstrakcyjna (Abstract factory)
    • Budowniczy (Builder)
    • Prototyp (Prototype)
  • Wzorce strukturalne
    • Adapter
    • Kompozycja
    • Proxy
    • Flyweight
    • Fasada (Facade)
    • Most (Bridge)
    • Dekorator (Decorator)
  • Wzorce czynnościowe/operacyjne (behawioralne)
    • Mediator
    • Łańcuch odpowiedzialności (Chain of responsability)
    • Obserwator (Observer)
    • Strategia (Strategy)
    • Komenda (Command)
    • Stan (State)
    • Wizytator (Visitor)

SOLID

Ten skrót już od kilku lat robi furorę w internecie oraz...podczas rozmów kwalifikacyjnych. Faktycznie to bardzo ważne aby trzymać się zasad, które opisuje ten akronim. Co więc oznacza SOLID? Rozbijamy go na poszczególne litery, które tłumaczymy z angielskiego tak:
  • S - Single responsibility principle - Zasada jednej odpowiedzialności

    Klasy powinny mieć ściśle określoną odpowiedzialność. Nigdy nie powinien istnieć więcej niż jeden powód modyfikacji klasy.

    Przykład

    W przypadku gdy klasa odpowiada za tworzenie obiektów na podstawie zadanego interfejsu (fabryka), nie powinna zajmować się niczym innym. Nie może nagle stać się miejscem aktualizacji danych, czy też wykonawcą innych zadań niezwiązanych z jej specjalizacją (jakim jest tworzenie obiektów danego interfejsu).
  • O - Open/closed principle - Zasada otwartości na rozszerzenie i zakmniętości na modyfikacje

    Kod powinien być tak napisany, by w przypadku rozwoju systemu i pojawiających się kolejnych wymagań biznesowych, nie trzeba było zmieniać istniejącego kodu. Ma on być otwarty na rozszerzanie i dodawanie nowych "klocków". Wymaganie to zmusza nas do dokładnego przemyślenia realizowanych zadań i wprowadzenia od początku na tyle ogólnych rozwiązań, by były one przygotowane pod dalszą rozbudowę.

    Przykład

    Implementując eksporter plików przygotowujemy metody, które będą abstrakcyjne i zapewnią możliwość wprowadzania kolejnych implementacji w postaci nowych klas w przyszłości. Każda taka klasa będzie dostarczać własną implementację eksportu. Inaczej będzie wyglądało wykonywanie eksportu do pliku csv, a inaczej do pliku xls, niemniej wszystkie eksporty będą posiadały ogólny zbiór podobnych zadań, takich jak pobranie danych, ich przetworzenie oraz eksport do pliku. Wspólne dla wszystkich będą pewne cechy, na przykład nazwa pliku, czy jego rozszerzenie.

    Nawet jeśli w pierwszej fazie projektu musimy przygotować eksport tylko do pliku csv, warto wprowadzić ogólny mechanizm eksportu, a implementacje metod dostarczyć w osobnych klasach. Jeśli nagle pojawi się wymaganie dodania eksportu xls, wówczas do wykonania pozostanie nam tylko przygotowanie klasy dedykowanej temu typowi eksportu. Nic nie zmieniamy w kontekście samego procesu (jego uruchomienia, obsługi błędów, czy obsługi zakończenia). Wszelkie zmiany nie mają również wpływu na kod eksportu csv (nie musimy go zmieniać).
  • L - Liskov substitution principle - Zasada podstawienia Liskova

    Tworząc kod powinniśmy go budować tak, aby zawsze działał zarówno dla samej klasy bazowej, jak i każdej implementacji.

    Przykład

    W przypadku wspomnianego eksportera możemy zrealizować nasz szczytny cel wydzielenia klasy bazowej, a następnie dostarczenia kolejnych implementacji eksportu w postaci niezależnych klas, ale jeśli nie stworzymy odpowiednio wartościowych metod operujących na każdej z klas eksportujących, wówczas i tak możemy nie spełnić zasady podstawienia.

    Na przykład jeśli w trakcie tworzenia klasy menedżera plików założymy, że będzie on działał dla każdej klasy rozszerzającej klasę bazową, ale jednocześnie w kodzie takiego menedżera wprowadzimy operacje specyficzne tylko dla plików xls, wtedy uruchomienie metody dla innych implementacji (na przykład dla pliku csv) nie zadziała lub wygeneruje błąd. I to znaczy, że nasz kod nie spełnia w tym miejscu zasady podstawienia Liskova.
  • I - Interface segregation principle - Zasada segregacji interfejsów

    Każdy interfejs powinien mieć wąski zakres specjalizacji. Dodatkowo powinny one być zwięzłe tematycznie, tak by maksymalnie ograniczyć występowanie pustych implementacji metod.

    Przykład

    Wprowdzamy do naszego eksportera interfejs przygotowujący raport zawierający prostą statystykę na temat wyeksportowanych elementów. Prawdopodobne wystarczy nam interfejs zawierający metody addReportData i createReport. Wtedy każdy eksporter implementując taki interfejs ma do dostarczenia zwięzłą listę metod. Nie ma tutaj metod nadmiarowych. Każda klasa zaimplementuje obie metody.

    Natomiast, gdybyśmy chcieli do takiego interfejsu dodać jeszcze metodę w stylu writeReportToFile, to mogłoby się okazać, że nie wszystkie klasy jej potrzebują. O ile każda z nich musi dostarczyć implementację tworzenia raportu, o tyle niektóre mogą na przykład "chcieć" zapisać wynik takiego raportu do bazy, albo wysłać taki raport za pomocą żądania REST. Wtedy metoda writeReportToFile pozostałaby pusta, a w samym interfejsie należałoby dodać metody w stylu writeReportToDB lub sendReportToExternalSource. Te metody byłyby jednak puste w przypadku klasy dostarczającej zapis do pliku.

    Takich problemów staramy sie unikać. Segregujemy interfejsy i tworzymy osobny interfejs, nawet z jedną metodą generyczną definiującą możliwe formy zapisu raportu - na przykład storeResults.
  • D - Dependency inversion principle - Zasada odwrócenia zależności

    Wysokopoziomowe moduły (bardziej abstrakcyjne) nie powinny zależeć od modułów niskopoziomowych (bardziej specjalizowanych). Innymi słowy abstrakacja nie powinna zależeć od implementacji.

    Przykład

    Co to oznacza w przypadku naszego przykładu? Jeśli po skonstruowaniu klasy bazowej z metodą export dodamy kilka klas dziedziczących, implementujących tę metodę, to wszystkie operacje doszczegóławiające export (czyli konkretne wykonanie exportu) powinny się odbywać w klasach dedykowanych każdemu z typów pliku.

    Jeśli nagle zaczniemy wprowadzać w metodzie export w klasie bazowej kod w stylu instrukcji if sprawdzającej czy mamy do czynienia z implementacją typu XlsExport czy CsvExport i tam dodamy wykonanie operacji zależnej od typu klasy, wówczas abstrakcyjna klasa bazowa zacznie zależeć od implementacji i nasza zasada nie będzie spełniona.
Tak naprawdę wielu programistów - często nawet nie do końca świadomie - stosuje się zwykle do części z wymienionych zasad, niemniej ważne jest, aby stosować się do nich wszystkich. Wtedy kod będzie dobrej jakości i nie sprawi większych problemów kolejnym programistom, którzy bedą go rozwijać w przyszłości. Tworzenie kodu w ten sposób jest oznaką profesjonalizmu i na pewno zostanie docenione przez innych członków zespołu.
Mapa umiejętności programisty Java

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy:
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .