Wzorce projektowe: Mediator(Mediator)

Dzisiaj miało być o wzorcu operacyjnym zwanym Mediator(Mediator), który dobrze spełnia ostatnią zasadę SOLID mianowicie unikania zależności między klasami, tutaj tworzymy jedną klasę, która można powiedzieć jest interfejsem do wszystkich elementów systemu, przekazujemy do tego interfejsu jedynie polecenia a on zajmuje się przekazaniem , mediator to na pewno wzorzec warty poznania…💪 no to jedziemy z tematem🙂.

Wstęp

Zaraz przejdziemy do konkretnego omówienia Mediatora, tylko mały news, po opublikowaniu tego wpisu może nie być kolejnego wpisu przez miesiąc, dwa miesiące albo więcej, ponieważ chciałbym się zająć bardziej tłumaczeniem tego bloga żeby nie tylko polacy mieli do niego dostęp oraz poprawieniem jego funkcjonalności oraz niektórych innych wpisów no i oczywiście mam też inne projekty oraz chciałbym trochę odpocząć😓, więc może to potrwać, blog traktuje chwilowo jako dodatek bo tak jak mówiłem mam inne projekty, ale powinno w przyszłości się to zmienić, ponieważ lubie pomagać innym. No dobra, przejdźmy do konkretów 🙂

 

Dyskusja

Wzorzec Mediator daje jednolity interfejs do przesyłania komunikatów między klasami, ten interfejs zawiera wszystkie referencje do tych klas, czyli po prostu zmniejsza ilość zależności między nimi, jeśli np potrzebujemy jakąś zależność lub element z innej klasy, wysyłamy polecenie do interfejsu mediatora, a ten zwraca nam odpowiednią wartość.

Gdybyśmy chcieli zapisywać zależności w tradycyjny sposób, czyli po prostu upychać zależności do klas, które tych zależności potrzebują, to wychodzi przez to plątanina zależności w tych klasach załóżmy, że mamy ludzi trzech ludzi, którzy są administorami, devami oraz użytkownikami, wygląd zależności wygląda tak jak poniżej na obrazku.

Mediator example

Przez takie tradycyjne działanie wychodzi plątanina zależności, w większych projektach jest to niedopuszczalne. A gdybyśmy zastosowali wzorzec Mediator to wyszłoby nam coś takiego:

Mediator example

I widać, że nie tylko wygląda to lepiej i jest o niebo czytelniejsze to przez taki układ obiekty nie wiedzą o sobie a im mniej obiekty wiedzą o sobie tym lepiej, jest mniejsze prawdopodobieństwo błędu a jeśli chcą pobrać jakąś zależność z innej klasy to po prostu wysyłają polecenie do interfejsu mediatora a ten im zwraca odpowiednią wartość.

 

Cel

  • Zmniejszenie ilości powiązań między klasami.
  • Hermetyzacja obiektów korzystających z interfejsu mediatora.
  • Udostępnienie jednolitego interfejsu do zarządzania zależnościami między klasami.

 

Problem

Najlepiej wykorzystać wzorzec mediatora w sytuacjach, gdzie mamy wiele zależności między klasami i tworzy się przez to tzw spaghetti code czyli kod, który trudno zrozumieć🙂. Najlepiej jak najszybciej nauczyć się jak pisać nie-spaghetti code 😐

 

Użyj wtedy kiedy:

  • Chcesz pozbyć się wielu zależności między klasami.
  • Chcesz zahermetyzować klasy tak żeby w ogóle nie wiedziały o sobie tylko pobierały zależności poprzez klasę mediatora.
  • Chcesz mieć jeden interfejs, w którym będą zgromadzone wszystkie referencje do klas.

 

Zalety i Wady

Zalety

  • Luźne zależności między obiektami w systemie.
  • Zależności między obiektami są elastyczne, możliwe jest łatwe rozbudowywanie zależności
  • Ponieważ cała logika jest zahermetyzowana w klasie mediatora, jeśli potrzebujemy dodać zależność do jakiejś klasy potrzebujemy jedynie rozszerzyć klasę mediatora.
  • Uproszczona komunikacja między klasami, ponieważ jeśli jakaś klasa chce się skomunikować z jakimiś innymi klasami musi jedynie wysłać polecenie do klasy mediatora.

Wady

  • Skomplikowanie  klasy Mediatora, jeśli będzie w niej za dużo zależności, które będą odpowiedzialne za wszystko, podobnie to działa na zasadzie pierwszej zasady SOLID czyli, jeśli w klasie Mediatora są zależności odpowiedzialne np tylko za komunikację to lepiej żeby nie było w tej klasie zależności odpowiedzialnych za coś innego, ponieważ wtedy klasy Mediatora będą bardzo skomplikowane.

 

Trochę tego jest, zdaję sobie z tego sprawę, dlatego walnę jakiś kawał, żebym i ja i ty odpoczął🙂😂

Przychodzi pacjent do lekarza i pyta się:

-Panie doktorze, ile ja jeszcze będe żył!?

-Hmm, pije Pan?

-Nie! Nigdy nie piłem!!

-Pali Pan?

-Nie! Też nigdy!

-Jakieś kobiety Pan miał?

-Też nie! Więc, Panie doktorze, ile ja będe żył?!

-No myślę, że z setkę … ale po c**j tak?

No dobra koniec kawału😂, przejdźmy dalej🙂

 

Struktura

 

Ogólny diagram UML wzorca Mediator wygląda tak:

Mediator scheme

Widać na nim, że klasy porozumiewają się poprzez klasę Mediator, jednak zwykle szczegółowa implementacja wzorca Mediator wygląda tak jak na diagramie poniżej:

Mediator scheme

W powyższym diagramie klasy dziedziczące po klasie Widget czyli Table, Tree, CheckBox korzystają z klasy Intermediary, która jest odpowiednikiem klasy Mediator z pierwszego diagramu.

W kodzie schemat wzorca mediator wygląda mniej więcej tak jak poniżej jak zwykle w dziale struktura podaje prosty przykład żeby łatwo było to zrozumieć, zacznijmy od klasy Mediatora:

Widzimy, że do metody Send()  wysyłane argumenty i w zależności od przekazanego typu klasy, przekazuje zmienną message do odpowiedniej klasy. Na samej górze mamy dwa razy ustawiony typ Colleague, żeby nie tworzyć typu klasy konkretnej, a już konkretne obiekty do klasy ConcreteMediator przekazujemy w kliencie.

No i jeszcze klasy ConcreteColleague1 i ConcreteColleague2.

Gdy wrócimy do klienta, na końcu są wywoływane metody Send() w klasach ConcreteColleague1 oraz ConcreteColleague2, które tylko przekazują wiadomość dalej do klasy ConcreteMediator i w zależności, która klasa wywołała metodę Send(), jest wyświetlana odpowiednia wiadomość z metody Notify().

Wynik:

 

Przykłady z życia wzięte

 

Chat

Pierwszym takim przykładem będzie chat, np gdyby osoby mielibyśmy podzielić w kodzie na klasy według każdego użytkownika chatu i dla każdego z nich mielibyśmy przypisywać zależności to strasznie poplątane byłyby zależności między nimi, i w tym przykładzie dobrze jest użyć mediatora, jeśli jakaś osoba wysyła wiadomość to jest ona przekazywana do klasy mediatora i następnie przekazywana do odpowiedniej osoby, która miała dostać wiadomość. I wszystkie zależności mamy w jednej klasie, pięknie proste, czytelne i skuteczne.

Zobaczmy jak to wygląda w kodzie, zacznijmy od klasy mediatora.

Zasada działania klasy mediatora jest taka sama jak w poprzednim przykładzie tylko implementacja się różni, tu nie mamy na sztywno przypisanych zależności tylko metodą Register() rejestrujemy na bieżąco zależności, sprawdzamy czy istnieje już w klasie taka zależność, jeśli nie to zapisujemy ją do słownika.

A w metodzie Send() sprawdzamy czy jest zarejestrowana osoba o imieniu, które przekazaliśmy do metody Send() w chacie, jeśli tak to wysyłamy do niej wiadomość.

Zobaczmy jeszcze jak wygląda klasa Participant, którą zdefiniowaliśmy w słowniku.

Najważniejsze jest to w tej klasie, że zapisujemy w niej nazwę uczestnika i przekazujemy wiadomość do metody Send() w klasie Participant i następnie przekazujemy wiadomość dalej do metody Send() w klasie Chatroom, oraz mamy zdefiniowane metody Receive() oraz Notify().

Jako, że uczestników podzieliłem ze względu na płeć, to mamy dla nich odrębne klasy.

I w tych klasach mamy zdefiniowane metody Receive() oraz Notify().

Na koniec klient.

I widać, że właśnie w kliencie rejestrujemy użytkowników chatu oraz wysyłamy wiadomości.

Wynik:

 

Wieża kontrolna

Kolejnym przykładem może być wieża kontroli lotu, założę się, że tam w oprogramowaniu stosują Mediatora🙂, wieża ta kontroluje lot każdego samolotu, helikopteru itp dobrze to obrazuje poniższy obrazek.

Mediator example

Zacznijmy tak jak w poprzednim przykładzie o chacie od klasy mediatora.

Zasada działania i implementacja identyczna jak w przykładzie chatu metoda Register() rejestruje zależności a metoda SendControlMessage() sprawdza czy przekazana maszyna istnieje w słowniku, jeśli tak to wyświetlamy odpowiednią wiadomość.

Zobaczmy jak wygląda klasa Machine

Bardzo podobna do klasy Participant tylko jest tutaj zdefiniowana metoda SendControlMessage(), jest ona zaimplementowana w klasach odpowiadających za typy maszyn, które są poniżej.

Widać, że w tych klasach wywołujemy metodę SendControlMessage() zdefiniowaną w klasie ControlTower do której przekazujemy nazwę maszyny i sprawdzamy w niej czy taka maszyna jest zarejestrowana.

I klient.

Wynik

Podobieństwa i wykorzystanie z innymi wzorcami

  • Mediator jest podobny do fasady pod tym względem, że uproszczają istniejącą funkcjonalność, ale różnią się tym, że Mediator jedynie upraszcza komunikację między klasami a Fasada tworzy interfejs, dzięki któremu będzie po prostu łatwiej korzystać z istniejących funkcjonalności.

Mediator jest jeszcze podobny do wzorca Polecenie(Command) oraz wzorca Obserwator(Observer), ale o tych podobieństwach powiemy sobie jak już je razem przerobimy.

 

Podsumowanie

I to wszystko na temat wzorca Mediator🙂

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

W następnym artykule, będzie mowa o wzorcu Metoda szablonowa(Template Method).

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🙂

I tak jak pisałem we wstępie chciałbym przetłumaczyć tego bloga oraz wprowadzić pare ulepszeń, więc może mi zająć to trochę czasu i gdy skończę to wtedy dam znać no i też chciałbym trochę odpocząć 🙂. Cóż, do zobaczenia po czasie nieokreślonym…😐

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

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

Post a comment

6 Comments to "Wzorce projektowe: Mediator(Mediator)"

avatar
  Subscribe  
newest oldest evaluated
Notify about
Jacek
Guest

Bardzo dobry wpis 😉 Dużo rozjasnia. Pozdrawiam

CodeFinger
Guest

Też chciałbym podziękować za wszystkie wpisy na temat wzorców projektowych. Kawał dobrej roboty. Przydało mi się bardzo.

CodeFinger
Guest

Sławek, możemy liczyć na kontynuację?