Skip to content

Witaj w Świecie Jutra!

  • Technologie jutra
  • Sprzęt jutra
  • Aplikacje jutra
  • Programowanie
  • Księga Drogi
  • Renowacja
  • Różności
  • Archiwum
  • Autor
  • Home
  • Wszystko
  • Wzorce projektowe – Observer
  • Programowanie
  • Wszystko

Wzorce projektowe – Observer

Jakub Raczkowski 18 stycznia 2024

Źródło obrazka: zmieniona okładka książki "Wielki Brat" George'a Orwella

Obserwator to jeden z najpopularniejszych wzorców projektowych. Tak wszechobecny, że niejednokrotnie wykorzystujemy jego zasady sami o tym nie wiedząc. Dzisiaj nauczymy się jak stosować go „by the books”.

Z observerem miałem po raz pierwszy do czynienia gdzieś pod koniec 30 poziomu na CodeGym. Było tam duże zadanie w którym tworzyliśmy symulację pracy restauracji – klasa typu fabryka tworzy nam randomowo zamówienia, po czym trafiają one do kucharzy którzy spędzają x czasu na ich przygotowaniu. Następnie te wirtualne potrawy są zabierane przez kelnera do wyznaczonego stolika.

Sięgając do definicji Obserwatora otrzymamy:

Definiuje pomiędzy obiektami relację jeden-do-wielu w taki sposób, że kiedy jeden obiekt zmienia swój stan, wszystkie jego obiekty zależne zostają o tym powiadomione i automatycznie zaktualizowane.

Rusz głową! Wzorce projektowe.

W wyżej przytoczonym zadaniu takim obiektem zmieniającym stan był Kucharz przygotowujący posiłki. Miał dwa stany: „wolny” i „zajęty”. Po upłynięciu iluśtam milisekund „spędzonych na gotowaniu”, zmienna „busy” otrzymywała wartość false o czym natychmiast zostawał poinformowany Kelner czyli nasz obserwator.

Jeszcze do wczoraj myślałem, że to był jedyny przypadek użycia Obserwatora przeze mnie. Byłem w błędzie. Dużym.

Obserwatorzy są na każdym kroku mimo, że sami ich nie zauważamy.

Otóż moi drodzy, cały frontend bazuje na tych czy innych implementacjach Observera.

Za każdym razem kiedy ustawiałem Mouse/Keyboard listener albo ActionListener przy JComponentach Swinga, tudzież EventListener do stworzenia klasy kontrolera w strukturze MVC – nieświadomie korzystałem z wzorca o którym dzisiaj mówimy.

Tym razem będzie inaczej – poniższy kod został przemyślany i napisany z pełną premedytacją!

Ale po kolei…

Najpotężniejszą klasą jaką kiedykolwiek przyszło mi grać we Wrota Baldura 2 jest bezsprzecznie dwuklasowiec Wojownik/Mag. Specjalnie nawiązuję do drugiej części gry, bo tam już na starcie mamy 8-9 poziom doświadczenia. Tworząc taką postać jako wojownika otrzymujemy sporą pulę punktów życia, potrafimy posługiwać się orężem i unikać ciosów wroga. Teraz możemy wybrać drugą klasę jako maga, relatywnie szybko ją „podekspić” i dostać dodatkowe benefity. Tworzymy w ten sposób bitewnego czarodzieja – śmiertelnie groźnego w zwarciu i na dystans, zdolnego przebić magiczne osłony przeciwnika i szybko zregenerować własne.

Niesamowicie przydatnym czarem już na początku jest „Kamienna skóra”. Mag rzuca ją na siebie i otrzymuje kilka warstw owej skóry. Teraz gdy przeciwnik w niego trafi, zamiast utraty punktów życia zdejmuje mu się jedna warstwa KS. Dopiero kiedy wszystkie warstwy znikną, wróg może dobrać się do „mięsa”.

Kamienna skóra. Źródło

Ale tylko teoretycznie. Gdyż z Kamienną skórą genialnie komponuje się zaklęcie „Warunkowanie” czyli znany z programowania „if”. Rzucając je wybieramy kiedy ma się uaktywnić i jaki inny czar będzie jego efektem. Możemy zatem śmiało stworzyć warunek:

Jeśli otrzymam jakiekolwiek obrażenia, rzuć na mnie Kamienną Skórę.

Tutaj tworzenie ulepszonego warunkowania przechowującego aż 3 zaklęcia. Źródło.

Podsumujmy:
Nie dość, że jesteśmy dobrze wyszkolonym wojownikiem którego trudno trafić, nie dość, że już na początku walki mamy ze 6 warstw kamiennej skóry, to jeszcze jak przeciwnik je zdejmie to się automatycznie odnawiają. A KS to tylko jeden z wielu czarów ochronnych jakimi dysponują czarodzieje z DnD.

Teraz spójrzcie jak taka walka wyglądałaby w kodzie z użyciem Obserwatora. Pełny kod tutaj.

public static void main(String[] args) {
        Wizard wizard = new Wizard();
        Orc orc = new Orc();
        wizard.addObserver(new Cleric());
        wizard.addObserver(new Contingency());

        orc.meleeAttack(wizard);
        orc.meleeAttack(wizard);
        orc.meleeAttack(wizard);
    }

I efekt potyczki:

Jak widzicie nie wygląda to na nic zbyt skomplikowanego, ale żeby dobrze zrozumieć co się dzieje pod maską musimy porównać powyższy przykład z poniższym, gdzie zakomentowałem obserwatorów.

Nikt naszego biednego czarodzieja już nie osłania. Ork jeździ po nim jak po burej kobyle. Pozostało już tylko mieć nadzieję, że kleryk ma przygotowany czar wskrzeszenia.

Dobrze, a teraz sprawdźmy resztę kodu i zobaczmy co tam się dzieje:

static class Character extends Observable{
        int hp = 10;
        void takeDamage(int damage){
            hp -= damage;
            System.out.println(hp);
        }
    }

    static class Wizard extends Character
(...)

Przede wszystkim nasze postaci: Mag, Kleryk, Ork dziedziczą po klasie Character co sprawia, że mogą się nawzajem atakować i leczyć. Natomiast sama klasa Character dziedziczy po Observable – klasie wyposażonej w takie metody jak addObserver() do dodania obserwatorów, setChanged() do zarejestrowania zmian oraz notifyObservers() do powiadomienia obserwatorów, że coś się zmieniło:

System.out.println("Czarodziej: Jestem ranny! Zostało mi tylko " + hp + "hp!");
                setChanged();
                notifyObservers();

Jak tylko czarodziej otrzyma jakiekolwiek rany zawiadamia o zmianie stanu (setChanged) po czym idzie z tym do „swoich ludzi”.

Jeżeli chodzi o samych obserwatorów to muszą oni implementować interfejs Observer, który zawiera metodę update(). Jest ona automatycznie wywoływana tuż po wysłaniu powiadomienia. U mnie pierwszym odbiorcą owego powiadomienia jest kleryk:

@Override
        public void update(Observable o, Object arg) {
            Character character;
            if (o instanceof Character) {
                character = (Character) o;
                heal(character);
            }
        }

Jak tylko kapłan dowiaduje się, że mag jest ranny – bierze się za leczenie.

Drugim obserwatorem jest czar Warunkowanie, który również uaktywnia się kiedy mag straci jakieś ha-pe-ki:

@Override
        public void update(Observable o, Object arg) {
            Character character;
            if (o instanceof Character) {
                character = (Character) o;
                castSpell(character);
            }
        }

Tutaj efektem otrzymania powiadomienia jest rzucenie nowej Kamiennej skóry, która chroni przed kolejnymi atakami ze strony orka.

ITCandidateEvaluator

Myślę, że już wiecie jak to wszystko działa. Tłumacząc zawiłości ja też miałem okazję sporo pomyśleć nad tematem i zastanowić się czy Observer będzie miał jakiekolwiek zastosowanie w moim drugim projekcie.

Wniosek – jak najbardziej. Znowu skorzystam z bibliotek Swinga do zwizualizowania swojego pomysłu. Ponadto jeszcze raz użyję architektury MVC z kontrolerem czekającym na jakiekolwiek sygnały ze strony widoku.

Coś czuję, że z obserwatorem zakumpluję się na dłużej, aczkolwiek niekoniecznie w wersji z bibliotek java.util. Sposób, który tutaj przedstawiłem jest już przestarzały – kłóci się z jedną z zasad poprawnej architektury oprogramowania:

„Przedkładaj kompozycję ponad dziedziczenie.„

Dziedziczenie jest super, ale to bardzo potężna broń, która ma jeden ładunek (na raz można dziedziczyć tylko po jednej klasie) dlatego jak z atomówką – w swoich projektach używajmy go w ostateczności, tam gdzie to faktycznie niezbędne.

Dziękuję za uwagę i zapraszam do następnego odcinka – z wzorcem „Dekorator”.

Tags: design patterns programming

Continue Reading

Previous: Wzorce projektowe – Strategy
Next: Wzorce projektowe – Decorator

Related Stories

Mageege Moon104 – test niskoprofilowego mechanika
  • Sprzęt

Mageege Moon104 – test niskoprofilowego mechanika

11 marca 2025
Przebranżowienie cz.4
  • Programowanie

Przebranżowienie cz.4

27 lutego 2025
Smartfon Jutra
  • Sprzęt

Smartfon Jutra

15 lutego 2025

Ze świata

  • Antyweb
  • Kwantowo
  • Dwóch po dwóch
Finałowy sezon kultowego sci-fi powróci szybciej niż myślisz! Padła data, jest też zwiastun!
Cieknący kran i zatkany odpływ? Parkside ma na to sposób
Masz telewizor od TCL? Szykuj się na fenomenalną nowość!
Nowa funkcja Allegro. Będzie za darmo dla posiadaczy Smart
Twórcy Linuksa nie nadążają. Tak wiele błędów do łatania jeszcze nie było
Polacy kochają tę funkcję Disney+. Niestety: nie wszędzie z niej skorzystają
Google odpala petardę. Użytkownicy Gmaila (i nie tylko) zapieją z zachwytu!
oto półprzewodniki pomogą nam zbudować technologie przyszłości
Dlaczego menu wygenerowane przez AI wywołuje obrzydzenie?
GPT-6 Astra już oficjalnie. OpenAI wita nas w "erze AGI"
Baseus sypie nowościami, jak z rękawa. Oto, co pokazali na IFA 2026
YouTube zmienia się w sklep. Nowa funkcja już w Polsce
Elektryk ostrzega: tych urządzeń nigdy nie podłączaj przez rozgałęźnik lub przedłużacz
Pierwsze PlayStation wraca. To zestaw stworzony dla graczy
Ten sprzęt z Action może szybko się wyprzedać. Przyda się w niemal każdym domu
Microsoft właśnie zepsuł jedną z najlepszych funkcji XBOXa. Wprowadza limity
Zdjęcia Google dostaną wspaniałą nowość. Zachwyci wielu użytkowników
Twój inteligentny zegarek kłamie. Zbadano, w czym nie można mu ufać
Dreame prezentuje zestaw wspaniałych nowości, które zadbają o to, by żyło nam się wygodniej!
Wkładasz pendrive powoli? Może przez to działać nawet znacznie wolniej!
Kosmiczna panorama i superkoronograf – co przyniesie nam Teleskop Nancy Roman?
O czym szepczą między sobą bozony Higgsa?
Wszechświat w trzech aktach – recenzja książki “Astronomia, kosmologia i astronautyka”
Wielka przerwa Wielkiego Zderzacza
A gdyby tak przeciąć foton na pół?
SpudCell – syntetyczna komórka, która udaje życie
Mega upał, omega manipulacja
Coraz więcej dowodów, coraz mniej pewności
Matematyczno-restauracyjny dylemat Ryszarda
Silnik produkujący informacje zamiast spalin
Tańszy Game Pass, droższy Steam Deck i Internet 0202122 – Odcinek #135
Elite Dangerous. Gra, z której odchodzi się po cichu
Gry zasługujące na adaptację filmową – Odcinek #134
Relacja z RePlay Fest 2026
Najlepsze intra i openingi w grach – Odcinek #133
AI, Star Trek, Rycerz Siedmiu Królestw, Festiwal dem na Steam Next Fest – Odcinek #132
Gry, filmy i seriale w 2025 i 2026 roku – Odcinek #131
30 lat konsoli PlayStation – Odcinek #130
Omawiamy serię The Walking Dead (gość: Stary Gracz)
Nikt nie potrzebuje cienkich smartfonów – Odcinek #129

To może cię zainteresować:

Mageege Moon104 – test niskoprofilowego mechanika
  • Sprzęt

Mageege Moon104 – test niskoprofilowego mechanika

11 marca 2025
Przebranżowienie cz.4
  • Programowanie

Przebranżowienie cz.4

27 lutego 2025
Smartfon Jutra
  • Sprzęt

Smartfon Jutra

15 lutego 2025
Czym jest Swagger?
  • Programowanie

Czym jest Swagger?

22 lipca 2024
  • Technologie jutra
  • Sprzęt jutra
  • Aplikacje jutra
  • Programowanie
  • Księga Drogi
  • Renowacja
  • Różności
  • Archiwum
  • Autor
Copyright © All rights reserved. | DarkNews by AF themes.