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 – Command
  • Programowanie
  • Wszystko

Wzorce projektowe – Command

Jakub Raczkowski 23 stycznia 2024

Zdjęcie z gry Commandos 2

Wzorzec Strategy polega na stworzeniu interfejsu implementowanego przez klasy, które zajmują się tym samym ale w inny sposób. Np. gdy chcemy pobrać listy użytkowników z różnych stron, z różnym API. Ale, ale! Po co ja to powtarzam skoro Strategia była omawiana na samym początku serii, a dzisiaj zajmujemy się Poleceniem?

Czytając o wzorcu Command, miałem podobne przemyślenia jak zajmując się Abstract Factory. Deja vu. Początek wydawał się bowiem zupełnie taki jak we wspomnianym Strategy. Wszystkie trzy wzorce wymagają albo interfejsu, albo chociaż współdzielenia klasy dziedziczonej. Różnice tkwią w szczegółach oraz w przeznaczeniu każdego wzorca:

  • Strategii powinniśmy użyć, kiedy mamy kilka algorytmów do osiągnięcia tego samego rezultatu (wybieramy jeden, najlepszy w danej sytuacji)
  • Factory najlepiej sprawdza się przy masowym tworzeniu podobnych obiektów (wybieramy szablon produkcji i uruchamiamy fabrykę)
  • Command, trochę jak Strategy, też oferuje kilka algorytmów, ale każdy służy do czegoś innego, możemy je zakolejkować, a nawet cofnąć

Mówiąc krótko:

Wzorzec Polecenie to ulepszony wzorzec Strategia.

Polecenie – hermetyzuje żądania w postaci obiektów, co umożliwia paramateryzowanie różnych obiektów zróżnicowanymi żądaniami (takimi jak np. żądania kolejkowania lub rejestracji) oraz obsługiwania operacji, które można wycofać.

Rusz głową! Wzorce projektowe

Wyobraźcie sobie, że macie jakiś obiekt. Niech to będzie odpowiedzialny np. za odtwarzanie plików wideo: „Player”. Jak myślicie – ile powinien mieć metod aby spełniał swoją funkcję?

Pomyślmy: odtwórz plik, zatrzymaj, wznów, podgłośnij, ścisz, przewiń, cofnij, wyjdź… Póki co wyszło nam już osiem, a to przecież tylko niezbędne minimum. Aby wyróżnić się na tle innych podobnych aplikacji trzeba dodać kontrolę jasności, szybkości odtwarzania, zoom i masę innych. Tymczasem klasa obiektu rośnie i się komplikuje.

VLC player, jeden z najlepszych odtwarzaczy

A jeśli każda z tych metod byłaby odrębną klasą? Np. mamy klasę Ścisz, która ma tylko jedną metodę execute() która ścisza audio i nic więcej nie robi. Klasa musi oczywiście implementować odpowiedni interfejs (czyli Command) podobnie jak wszystkie inne klasy-polecenia. Wtedy wystarczy, że przed skorzystaniem ze ściszenia w Playerze, wyślemy temu obiektowi odpowiednie polecenie (setCommand()) które ustawia tę jedyną metodę klasy Player na wywoływanie execute() obiektu Ścisz.

Z jednej strony wydaje się to fajną sztuczką zapewniającą wysoką hermetyzację, ale z drugiej wygląda na prosty przepis aby utonąć w ilości tworzonych klas-komandosów (stąd grafika tytułowa, heh). Rozwiązanie jest owszem, fajne – ale tak jak z Singletonem, należy go używać z umiarem – wtedy kiedy rzeczywiście rozwiązuje jakiś problem.

Ale po kolei…

Parę odcinków temu, przy okazji omawiania wzorca Observer, tłumaczyłem wam zasadę działania znanego z DnD czaru Warunkowanie. Nie będę się powtarzał, ważne jest jednak to, że w Warunkowaniu umieszczaliśmy inny czar, który aktywował się w wybranej wcześniej sytuacji, na wybranym wcześniej celu.

Z jednej strony wygoda bo pełna automatyka, ale z drugiej – trochę nas to ograniczało, bo po inicjalizacji traciliśmy kontrolę nad zawartym zaklęciem. A co jeśli moglibyśmy sami wybrać czas i miejsce aktywacji?

Powiecie: głupota! Przecież tak właśnie działają zaklęcia bez żadnych warunkowań! Wybieram kulę ognia i strzelam w bandę goblinów dopiero gdy atakują kupą, wybieram kamienną skórę i rzucam na siebie dopiero kiedy dochodzi do walki w zwarciu.

A gdyby do takiego nie-automatycznego warunkowania można by włożyć więcej niż jeden czar?

O! I tu pojawia się potencjał! Jeśli mamy do czynienia z potężnym przeciwnikiem, to często już na starcie jest chroniony magią ochronną. Kamienna skóra blokuje obrażenia fizyczne, Ochrona przed żywiołami sprawia, że kula ognia też nie zada żadnych obrażeń. A Przyśpieszenie sprawi, że sam będzie atakował dwa razy szybciej.

W takich sytuacjach w Baldurze najlepiej sprawdzały się tzw. Sekwencery czyli właśnie ulepszone Warunkowania, zdolne zakolejkować i odpalić szybko jeden po drugim aż trzy zaklęcia. Moglibyśmy tam zatem umieścić kolejno Wyłom, Wrażliwość i Spowolnienie, aby usunąć z wroga wszelkie buffy.

Ale jak coś takiego wyglądałoby w kodzie (pełna wersja tutaj) wykorzystującym wzorzec Polecenie?

Na starcie potrzebujemy czarodzieja, któremu wystarczy jedna metoda.

static class Wizard{
        
        Commands currentCommand;
        
        void castSpell(Enemy enemy){
            System.out.print("Czarodziej rzuca czar: ");
            currentCommand.engage(enemy);
        }
}

Do tego interfejs implementowany przez różne polecenia (będące tutaj zaklęciami):

interface Commands{
        void engage(Enemy enemy);      
    }	
	
static class FireBall implements Commands{
      
        @Override
        public void engage(Enemy enemy) {
            System.out.println("Kula ognia.");
            enemy.hp -= 33;
        }

I już możemy zaobserwować efekty:

Czarodziej rzuca kulą ognia – Poleceniem w lodowego giganta.

Ale to nie wszystko, bo wcześniej wspomniałem:

„Command, podobnie jak Strategy, też oferuje kilka algorytmów, ale każdy służy do czegoś innego, możemy je zakolejkować, a nawet cofnąć.”

Cofnąć. Wydaje się trudne, a wystarczy do interfejsu Commands dodać drugą metodę, np. revert() która będzie odwrotnością tej pierwszej. Zatem jeśli Kula ognia zadaje 33 punkty obrażeń, to revert ma tyle samo przywracać:

public void revert() {
            enemy.hp += 33;
        }

Co fajne, kolejne polecenia możemy zakolejkować np. dodając je do stosu (Stack<>), a następnie wycofać wszystkie jednym ruchem, co w naszym wypadku byłoby swoistym wczytaniem stanu gry (loadGame()):

Wszystkie polecenia trafiają na stos, więc bardzo łatwo je odwrócić jedną metodą.

Rzeczona kolejka prowadzi nas też wprost do Sekwencera – zamiast wywoływać castSpell() możemy wrzucić wszystkie zaklęcia na stos (addToSequencer) i odpalić je razem metodą fireSequencer().

void fireSequencer(){
            System.out.println("Odpalono sekwencer zaklęć:");
            for (Commands command : allCommands) {
                command.engage(currentEnemy);
            }
        }
Możemy też zakolejkować polecenia bez wywoływania i odpalić je razem kiedy nam pasuje.

ITCandidateEvaluator

Teraz standardowe pytanie – czy widzę użycie wzorca Command w moim kolejnym projekcie?

Jeszcze nie, ale to się może zmienić.

Wydaje mi się, że Polecenie może wyjść w praniu przy refaktoryzacji kodu gdzieś pod koniec prac nad aplikacją. Np. jeśli zobaczę potrzebę umieszczenia przycisku „Cofnij” usuwającego ostatnio wprowadzoną zmianę. Albo gdy będę chciał hermetyzować niektóre metody danej klasy, tak by łatwo było mi je zmienić lub dodać kolejne w przyszłości (reguła: Otwarty/Zamknięty).

Samo kolejkowanie wprowadzonych poleceń też może się przydać w zakresie logowania informacji oraz błędów.

Na dzisiaj tyle i jak zwykle dziękuję za uwagę. Widzimy się już niedługo przy wzorcach Adaper oraz Fasada.

Tags: design patterns programming

Continue Reading

Previous: Wzorce projektowe – Singleton
Next: Wzorce projektowe – Adapter

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
Sztuczne inteligencje mają swój serwis społecznościowy. Oto Moltbook
Fani serialu oszaleją. Limitowany zestaw w McDonald’s
Dane z sondy Cassini możesz obejrzeć w domu. Wystarczy wejść tutaj
T-Mobile z Orange walczą o serca kinomanów
PIT za 2025 rok – od kiedy można rozliczać, by szybciej dostać zwrot podatku
Erli pozywa Allegro - konflikt w polskim e-commerce trwa w najlepsze
Klasyczne Naruto powraca. Już wkrótce obejrzysz na Canal+
LEGO świętuje walentynki. Takich atrakcji nie można przegapić
Oto Linux Lite 7.8. Mnóstwo nowości, które warto docenić
KSeF w ogniu. Rząd: wprowadzamy tymczasowe rozwiązanie
Lidl upadł na głowę. Już teraz wyprzedaje wiosenne elektronarzędzia
Jedna funkcja, mnóstwo korzyści. iPhone pompuje bezpieczeństwo w iOS 26.3
NASA odpala silniki. Od tego testu zależy misja na Księżyc
Lidl rozpieszcza klientów Orange. 1000 GB za jedno kliknięcie
Windows składa broń? Zrezygnują z krytykowanej decyzji
Rewolucja w budownictwie. Oto beton nowej generacji
Samsung staje na głowie, by Galaxy S26 nie zdrożał. W grę wchodzi tylko jedna opcja
Profil zaufany nie działa. Awaria systemu usług publicznych
Harry Potter: padła data premiery serialu HBO!
Firma sprowadzająca samochody z USA - jak wybrać specjalistę i nie stracić pieniędzy?
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.