Wzorce projektowe: Łańcuch zobowiązań(Chain of responsibility)

Dzisiaj będzie temat, tak jak obiecałem w poprzednim artykule, o wzorcu nazywanym łańcuchem zobowiązań, zwany też łańcuchem odpowiedzialności, świetnie się nadaje np do nadawania obowiązków każdemu pracownikowi w firmie, no to luu…🙂 jedziemy z tematem 🙂

 

Wstęp

Na początek słowo wstępu, trochę osób mi zarzucało, że jest trochę zbyt skomplikowana terminologia w moich wpisach i nie poleciliby tego początkującym a dokładniej takim, którzy dopiero się uczą programować. Oczywiście, że wzorce projektowe nie ✋ są dla dopiero początkujących w programowaniu, głównie dlatego, że początkujący zwykle robią wszystko w sposób, „A byleby działało”, wzorce projektowe są dla osób, którym zdarzyło się napisać trochę brudnego 😥 kodu i takie osoby już będą wiedzieć, że robienie wszystkiego w sposób „byleby działało” ma bardzo krótkie nogi.

No i jak ktoś nie wie co to jest klasa abstrakcyjna albo interfejs to też niech lepiej się nie bierze za wzorce projektowe bo te pojęcia są wykorzystywane w większości wzorców. Staram się pisać prostym językiem, ale w programowaniu a szczególnie we wzorcach projektowych jest to bardzo trudne i żeby dobrze zrozumieć dany wzorzec czasem trzeba trochę tej skomplikowanej terminologii użyć, jeśli jednak trochę się zapędze i będzie coś niezrozumiałego nawet dla osób, które już troche siedzą w temacie, to oczywiście jestem otwarty na wszelkie uwagi, skontaktować się ze mną można na stronie Kontakt lub po prostu pisać w komentarzach. I to byłoby wszystko we wstępie. Przejdźmy do omówienia wzorca.

 

Dyskusja

Łańcuch odpowiedzialności służy do przekazywania zadań w łańcuchu  obiektów(stąd nazwa wzorca) i w zależności od otrzymanego zadania obiekt, który dostał zadanie sprawdza czy spełnia warunek wykonania otrzymanego zadania, jeśli nie to przekazuje żądanie dalej, do kolejnych obiektów. Obrazuje to dobrze poniższy obrazek.

Chain of responsibility example

Klient wysyła żadąnie i wszystkie obiekty po kolei sprawdzają czy są upoważnione do tego by wykonać to żadanie a główna część implementacji wzorca składa się z listy jednokierunkowej i klasy abstrakcyjnej, która można powiedzieć, że jest szablonem tej listy.

I właśnie, żeby zrozumieć ten wzorzec trzeba wiedzieć co to jest lista jednokierunkowa.

 

Lista jednokierunkowa

Lista jednokierunkowa, jest to struktura w której każdy element zna adres następnego elementu.

W platformie .NET jest gotowa zaimplementowana lista jednokierunkowa, dla tych co piszą w C++ tu jest przykład listy jednokierunkowej, również w innych językach implementacja może wyglądać inaczej, coż, każdy musi dopasować implementację listy jednokierunkowej do swojego języka. Ale ogólnie opis wszystkich przykładów wzorców projektowych można spokojnie przenieść z C# na swój język Python, C++, Java itp. 🙂 Wzorce projektowe i praktyki pisania czystego kodu przydadzą się każdemu niezależnie od języka.

Przykład poniżej:

To jest przykład listy jednokierunkowej, każdy element tej listy zna adres następnego elementu, w powyższym przykładzie najpierw dodajemy elementy do listy na końcu i na początku, potem do pierwszego elementu listy dodajemy kolejny element i znowu do poprzednio dodanego elementu dodajemy kolejny element, zauważ co się dzieje, każdy element listy wie jaki jest jej kolejny element.

Wynik:

Na podobnej zasadzie działa wzorzec łańcucha zobowiązań w ten sposób wiadomo gdzie przekazywać żadania klienta, w tym wypadku do kolejnego obiektu w strukturze.

Chyba wiem jakie czasem masz uczucia jak się uczysz wzorców projektowych, pewnie jak ten Pan poniżej🙂

Spokojnie, nie jest to tak straszne wbrew pozorom, przejdźmy dalej 🙂

 

Cel

  • Obsługa wysłanego żądania przez łańcuch obiektów, który zapewnia obsługę żądania lub przekazuje go dalej.
  • Wysłanie żadania do łańcucha, który zawiera wiele handlerów(obiektów, które mogą obsłużyć żądanie)

 

Problem

Wzorca łańcucha obiektów możesz użyć np tak jak było we wstępie powiedziane do zarządzania zadaniami, lub wszędzie tam gdzie są mechanizmy z podobnymi żądaniami lub wyobraźmy sobie taką sytuację gdzie musimy efektywnie obsłużyć dużą ilość żądań bez konieczności pisania klas z zależnościami.

Chain of responsibility example

 

Użyj wtedy kiedy:

  • Chcesz szybko i łatwo przydzielić zadania do poszczególnych obiektów.
  • Chcesz np zaimplementować mechanizm przetwarzania żądań w którym łatwo będzie dodawać obsługe nowych typów oraz ich ewentualne usuwanie w razie potrzeby.

 

Struktura

Diagram wzorca łańcuch zobowiązań składa się z:

  • Klasy abstrakcyjnej Handler w której jest zdefiniowana metoda handle() przekazująca dalej żądanie jeśli do danego obiektu żądanie nie pasowało oraz listy jednokierunkowej, której elementami są obiekty w łańcuchu, każdy obiekt wie jaki jest kolejny obiekt w liście.
  • Klas, które dziedziczą po klasie Handler i które przetwarzają żądania, jeśli żądanie pasuje przetwarzają je, jeśli nie przekazują dalej do kolejnego obiektu w kolejce.

Chain of responsibility scheme

Poniżej, przykład wzorca łańcucha. Jest prosty, żebyś mógł łatwo go zrozumieć.

Odpowiednikiem klasy Handler z diagramu UML jest klasa Number. To ona przechowuje obiektu z łańcucha oraz definiuje metodę przekazującą żądania dalej.

Klasami łańcucha są klasy One, Two, Three.

Wszystkie te klasy dziedziczą po klasie Number, dzięki temu mogą przekazywać żadania dalej.

No i uruchomienie tego w kliencie.

W kliencie najpierw tworzymy instancje klas należących do łańcucha, oraz zapisujemy te obiekty w sposób właśnie listy jednokierunkowej, żeby kolejny obiekt do którego przekazujemy żądanie wiedział jaki jest następny, właśnie po to żeby wiedział do jakiego kolejnego obiektu przekazać żądanie. Typy żądań zapisaliśmy w typie wyliczeniowym enum, w liście QuestsOnToday.

Na końcu przekazujemy po kolei żądania w pętli foreach.

Wynik:

 

Zalety

  • Klient gdy wysyła żądanie nie wie, który z obiektów będzie je obsługiwał.
  • Możliwość łatwego i elastycznego przydziału odpowiedzalności do obiektów Handle (czyli obiektów, które obsługują żądania klienta).

 

Wady

  • Brak gwarancji obłsugi żądania, przez to również działanie łańcucha jest trudniejsze do debugowania.

 

Przykład

Wypłaty dla pracowników

W tym przykładzie będziemy sprawdzać wpłaty jakiej wysokość kto na jakim stanowisku powinien dostać czyli, jeśli wpiszemy w konsoli 4000 to sprawdzamy, że wypłaty wysokość 4000 dostaje np dyrektor.

Odpowiednik klasy Handler z diagramu UML wygląda tak.

Łańcuch obiektów wygląda tak.

Widzimy, że wygląda podobnie jak w poprzednim przykładzie ze schematem, ale jednak trochę się różni, głównie tym, że metoda przekierowująca ma już ciało w klasie abstrakcyjnej PurchasePower i nie jest rozbudowywana przez każdy obiekt w łańcuchu.

Spójrzmy jak to wygląda w kliencie.

Również wygląda podobnie jak w poprzednim przykładzie, ale różni się tym, że w pętli while gdy wpiszemy wartość jest ona zapisywana w obiekcie klasy PurchaseRequest i dopiero potem wysyłana do obietu klasy PurchasePower do metody processRequest() .

A zobaczmy jak wygląda klasa PurchaseRequest.

Widać, że w tej klasie są zapisywane dane odnośnie wypłaty, możemy ją wyświetlać lub ustawiać. Przeanalizuj sobie dokładnie ten przykład, jak zwykle będzie link na końcu lekcji w podsumowaniu do githuba ze wszystkimi przykładami.

Wynik

 

Przykład z życia wzięty

Wypłata z bankomatu

Zrobimy kolejny przykład na zasadzie wypłacania pieniędzy 💵 z bankomatu, będziemy wysyłać żądania, które będą sprawdzały co wybrał użytkownik.

Chain of responsibility example

Najpierw klasa abstrakcyjna Pay, która jest szablonem listy jednokierunkowej i jest w niej zdefiniowana metoda, która przekierowuje żądania do następnych obiektów.

Oraz rzecz jasna łańcuch obiektów, które sprawdzają wysyłane żądania.

Oraz uruchomienie wszystkiego w kliencie.

Czyli procedura podobna jak w pokazanym wcześniej schemacie wzorca łańcucha, mamy klasę abstrakcyjną Pay, która jest odpowiednikiem klasy Handler z diagramu UML oraz mamy łańcuch klas, które dziedziczą z klasy Pay. Oraz tworzymy instancje klas w kliencie i zapisujemy je na zasadzie listy kierunkowej, no i wiadomo klient wybiera opcję ile pieniędzy chce wypłacić w zależności od tego co wybrał wysyłamy żądanie, które każdy obiekt w łańcuchu po kolei sprawdza, zaczynając od klasy PayHundred bo to ona jest pierwsza w kolejce.

Wynik

 

Podsumowanie

I to wszystko na temat wzorca Łańcuch zobowiązań🙂

Jeśli wytrwałeś do końca to gratuluje 👏. Jeśli na tym blogu czytałeś o innych wzorcach projektowych i je przerobiłeś i wszystkie rozumiesz to już nie jesteś jakimś początkuącym programistą, tylko masz już pewnego skilla. 👍

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

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

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🙂

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

Post a comment

1 Comment to "Wzorce projektowe: Łańcuch zobowiązań(Chain of responsibility)"

avatar
  Subscribe  
newest oldest evaluated
Notify about
Tomek
Guest

Czyli jednym zdaniem kolejka komend (komenda – wzorzec projektowy).
Przykład z życia – obsługa zdarzeń w GUI.