Wzorce projektowe: Polecenie(Command)

Dzisiaj o wzorcu trochę bardziej skomplikowanym od poprzedniego czyli o wzorcu Polecenie(Command), jest bardzo podobny do wzorca łańcuch odpowiedzialności(Chain of responsibility) jednak trochę się różni, ma bardzo podobne przeznaczenie jak łańcuch odpowiedzalności, czyli przekazywanie żądań, ale w razie potrzeby może je jeszcze zapisywać na stosie w celu np cofnięcia poprzednio wykonanej operacji, dokładniej o tym wzorcu dalej w artykule 🙂

 

Dyskusja

Głównym celem tego wzorca jest zahermetyzowanie wszystkich danych potrzebnych do wykonania żądania, zwykle tymi danymi jest obiekt dzięki czemu parametry tych żadań mogą być zmieniane w zależności od rodzaju odbiorcy, oraz celem tego wzorca jest również zapisywanie żadań na stosie lub w kolejce w celu przeglądania historii tych żądań lub w razie potrzeby cofania tych żądań.

Najważniejsze elementy w implementacji wzorca Command to:

  • interfejs Command, który posiada zdefiniowaną metodę execute().
  • Klasa ConcreteCommand, która rozszerza interfejs Command, rozszerzając metodę execute().
  • Klasa Receiver, komunikująca się z klasą ConcreteCommand, przekazująca żądania klienta.
  • Klient, który tworzy obiekt klasy ConcreteCommand oraz ustawiający klasę Receiver.

Jest wiele przykładów gdzie można użyć wzorca Command 🙂 Można go użyć tak jak wspomniałem wcześniej w jakichś aplikacjach, które wymagają przeglądania historii lub cofania albo dodawania jakichś akcji, można go użyć też np w progress barze, czyli w pasku postępu a wiadomo do czego służy 🙂 oczywiście do przeglądania postępu wykonywania jakichś żądań, często jest też wykorzystywany do wykonywania akcjii po kliknięciu w np przycisk. itd itd 🙂

Jak widać zastosowań wzorca Command jest wiele 🤔 zwłaszcza w tych dużych projektach.

 

Cel

  • Enkapsulacja żądania w obiekcie.
  • Zmiana parametrów żądania w zależności od rodzaju odbiorcy.
  • Możliwość zapisywania żądań w kolejce.

 

Problem

Musisz np wysłać żądania do obiektów bez znajomości tych żądań lub obiektów odbierających żądanie.

 

Użyj wtedy kiedy:

  • Potrzebujesz wglądu do historii wykonywanych żądań lub w miare potrzeb, cofania ich.
  • Musisz zahermetyzować wszystkie dane, które chcesz wysłać.
  • Musisz zmienić parametry tych żądań bez naruszania zasady open-closed.

 

 

Struktura

Standardowo diagram UML wzorca Command:

Raczej nie muszę się powtarzać, już wyjaśniłem o co mniej więcej chodzi w implementacji w dziale dyskusja na samej górze.

Interfejs CallbackInterface to interfejs Command a reszta klas, które rozszerza ten interfejs to ConcreteCommand.

Przejdzimy w końcu do prawdziwego kodu 🙂

 

Przykład

Przełączanie lampy

Prosty przykład włączania i wyłączania lampy żebyś mógł łatwo zrozumieć ten wzorzec

Zacznijmy od interfejsu ICommand.

Posiada metodę zdefiniowaną metodę Execute(), nic nadzwyczajnego 🙂

Klasy, które rozszerzają interfejs ICommand to CloseSwitchCommand oraz OpenSwitchCommand.

Najpierw CloseSwitchCommand.

Oraz OpenSwitchCommand.

Też nic trudnego 🙂 klasy te wywołują metody w zależności od tego czy chcemy włączyć lampę czy ją wyłączyć.

Widać też w tych klasach zdefiniowany interfejs ISwitchable po to żeby nie tworzyć typu klasy konkretnej. Pamiętamy zasadę Dependency inversion 🙂

Zobaczmy co jest w interfejsie ISwitchable.

Również nic trudnego 🙂 Mamy zdefiniowane metody włączania i wyłączania lampy 🙂

Jedźmy dalej 🙂 do klasy Receiver w naszym przypadku nazywa się ona Switch.

Widzimy, że ta klasa komunikuje się z klasami CloseSwitchCommandOpenSwitchCommand, które przekazujemy do konstruktora i w ten sposób możemy wywoływać ich metody.

No i klasa Light, która wyświetla odpowiednie komunikaty i zapisuje stan lampy.

Na koniec klient.

W kliencie tworzymy obiekty tych klas i wsadzamy je do odpowiednich konstruktorów innych klas. I zobaczymy odpowiednie komunikaty w zależności od tego co wpiszemy w konsoli.

Wynik:

Część z was może spytać „A po co to tak komplikować? Nie lepiej zrobić to tak?”

Cóż, główną motywacją używania wzorca Command jest zahermetyzowanie wszystkich danych potrzebnych do wykonania żadania, w poprzednim przykładzie wszystko jest zahermetyzowane w żądaniach oraz mamy ładnie oddzielny kod wysyłania żądań oraz kod odbierający te żądania co daje nam ładniejszy oraz bardziej elastyczny kod no i oczywiście mamy możliwość monitorowania żądań, ale i tak myślę, że ci którzy nie robią jakiś dużych projektów powinni na razie mieć ten wzorzec, że tak powiem „z tyłu głowy”. Wzorca command najlepiej używać w dużych projektach. W tych mniejszych i średnich projektach powinniśmy kierować się zasadą KISS

 

Przykład z życia wzięty

Restauracja- zamawianie jedzenia

Załóżmy, że musimy zrobić system zapisujący wszystkie działania zamawiania jedzenia od zamówienia przez klienta do trafienia zamówienia do kucharza oraz chcemy mieć możliwość zobaczenia zamówień, które są w kolejce, dobrze jest użyć wzorca Command w tym przykładzie. Dla zobrazowania sytuacji jest obrazek poniżej 🙂

Zaczynamy od klasy klienta Customer.

I interfejs IOrder

Wiadomo klient składa zamówienie kelnerowi, więc przechodzimy do klasy Waiter.

Interfejs ICommand

Kelner zapisuje zamówienie, czyli w naszym przypadku do klasy Order.

Interfejs ICookAble

I przekazuje je kucharzowi, czyli klasie Cook.

Naturalnie kelner dał kartkę z zamówieniami kucharzowi, więc on może je wszystkie odczytywać. Wszystkie zamówienie oczywiście zapisujemy w kolejce, zgodnie z tym, który klient pierwszy złożył zamówienie 🙂

Pięknie proste i przyjemne 🙂

I tworzymy wszystkie obiekty w funkcji Main.

Wynik:

 

Powiązania z innymi wzorcami

  • Wzorce Chain of Responsibility, Command, Mediator, and Observer opisują w jaki sposób możesz rozdzielić klasy wysyłające żadania oraz klasy je odbierający, ale z różnymi kompromisami 🙂
  • Wzorzec Chain of Responsibility może użyć wzorca Command do reprezentowania żądań jako obiekty.
  • Czasem niektóre działania, które chcemy zapisać do historii musimy skopiować działa to jak wzorzec Prototype.

 

Podsumowanie

I to wszystko na temat wzorca Polecenie(Command)🙂

Link do githuba ze wszystkimi przykładami: https://github.com/Slaw145/Command

W następnym artykule, będzie mowa o wzorcu Stan(State).

Standardowo, przypominam o newsletterze, którym wysyłam powiadomienia o nowych wpisach oraz dodatkowe informacje na temat, ogółem mówiąc, świecie IT.

KONIECZNIE dołącz do społeczności DevmanCommunity na fb, części społeczności jest w jednym miejscu 🙂

-strona na fb: Devman.pl-Sławomir Kowalski

-grupa na fb: DevmanCommunity

Pytaj, komentuj pod spodem na końcu wpisu, podziel się nim, oceń go, rób co wolisz🙂

Ilustracje, obrazki oraz diagramy są z: https://sourcemaking.com/design_patterns/command

 
Jeśli ten wpis ci się przydał podziel się nim ze swoimi znajomymi :)

Post a comment

2 Comments to "Wzorce projektowe: Polecenie(Command)"

avatar
  Subscribe  
newest oldest evaluated
Notify about
CodeFinger
Guest

Zostały Ci dwa wzorce. Never ever give up man 🙂