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
SpaceX zmienia priorytety. Lądowanie na Księżycu już w przyszłym roku
InPost sprzedany. Polska firma w rękach amerykańskiego giganta
Premiery Apple tuż za rogiem. Padły konkretne daty!
Quentin Tarantino ma coś specjalnego dla Netflixa. Fani już mogą zacierać ręce
Nowy iPhone tuż za rogiem. Dużo dobrych wieści na jego temat
WhatsApp odpala petardę. Nowa funkcja trafia do użytkowników
YouTube wkurzył użytkowników. Za lubianą funkcję teraz trzeba płacić
Mamy artylerię w domu, czyli AHS Krab. Co potrafi polska haubica samobieżna?
Ile RAM-u naprawdę potrzebuje twój komputer?
To tu PlayStation zarabia miliardy. Taki sukces budzi trudne pytania
Parkside czuje już wiosnę w powietrzu. Roboty koszące tanie jak nigdy
Recenzja Terramaster SSD D1 Pro. Gdy liczy się przede wszystkim szybkość...
Akumulatory będą tańsze. Jest na to sposób
mObywatel z nowymi dokumentami. Kto skorzysta?
GOG.com stawia na różnorodność. Polska platforma wie jak rozpieszczać
Garmin funduje duże obniżki na Walentynki. Wyróżnia się szczególnie jeden model
Masz Amazon Prime? Takiej okazji nie można przegapić
Microsoft ma dla graczy rekomendację. Tanio nie będzie
iPhone 18 z rewolucyjną zmianą. Wszyscy docenią
Nie wyrzucaj starego smartfona – daj mu drugie życie!
To by było na tyle, jeśli chodzi o możliwość ugody
Ocalić od zapomnienia
Ostatni kwant
ALH 84001 – meteoryt, o którym mówiono nawet w Białym Domu
HESS zarejestrował kosmiczny elektron o niespotykanej energii [Phys. Rev. Lett.]
Matka ciemnej materii – recenzja biografii “Vera Rubin. Życie”
Satelita, który zerwał się ze smyczy
Wiadomość od Carla Sagana do przyszłych eksploratorów Marsa
Ile najdłużej może trwać zaćmienie Słońca?
Nowa największa liczba pierwsza ma ponad 41 milionów cyfr [GIMPS]
30 lat konsoli PlayStation – Odcinek #130
Omawiamy serię The Walking Dead (gość: Stary Gracz)
Nikt nie potrzebuje cienkich smartfonów – Odcinek #129
To ostatni dzwonek na kolekcjonowanie gier i filmów
Najlepsza relacja z PGA 2025 (Poznań Game Arena)
Bumblebee wśród klawiatur. Marvo Meqa 80W – recenzja
Pierwsze spotkanie z Omoda 7 Super Hybrid
Logitech MX Master 4, Wednesday, 1670 sezon 2 – Odcinek #128
Tani pad, który chciał być jak DualSense. Test Monka Contra GT-96
Secret Service i prasa komputerowa w Polsce – Odcinek #127

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.