Wzorzec projektowy mediator
Cel
Uproszczenie komunikacji wielu obiektów
Hermetyzacja mechanizmu wymiany komunikatów.
Mediator znajduje zastosowanie w sytuacji, gdy wiele obiektów o wspólnym interfejsie musi komunikować się ze sobą w celu wykonania określonego zadania. Najprostszym, lecz trochę naiwnym rozwiązaniem jest powiązanie wszystkich obiektów ze sobą w topologii grafu pełnego. Takie rozwiązanie jest jednak słabo skalowalne: dołączenie kolejnego obiektu powoduje konieczność powiadomienia o zmianie wszystkich pozostałych, aby potrafili skomunikować się z nowym uczestnikiem interakcji. Ponadto powoduje, że mechanizm komunikacji jest rozproszony, co utrudnia jego modyfikację i dalszy rozwój.
Struktura
Obiekty Colleague i obiekt Mediator tworzą topologię gwiazdy. Komunikaty między obiektami Colleague są przekazywane za pośrednictwem mediatora
Wzorzec Mediator proponuje topologię gwiazdy, w której centrum znajduje się właśnie obiekt Mediator. Posiada on referencje do pozostałych obiektów (Colleague) i zna ich zakres odpowiedzialności. Komunikacja pomiędzy obiektami Colleague wymaga pośrednictwa Mediatora, który potrafi przekazać komunikat do właściwego odbiorcy.
Uczestnicy
- Mediator
- definiuje interfejs dołączania i odłączania kolegów
- Concrete Mediator
- implementuje mechanizm komunikacji pomiędzy obiektami Colleague
- posiada referencje do zarejestrowanych obiektów Colleague
- Colleague
- definiuje wspólny interfejs dla komunikujących się obiektów
- posiada referencję do obiektu Mediator
- komunikuje się z innymi obiektami za pośrednictwem obiektu Mediator
Mediator posiada metody służące do dołączania i odłączania obiektów Colleague. Ponadto jego zadaniem jest implementacja mechanizmu komunikacji, czyli podejmowanie decyzji który z obiektów Colleague powinien wykonać określone żądanie. Obiekty Colleague nie są obciąone zadaniem komunikacji z pozostałymi obiektami. Ich wiedza jest ograniczona do znajomości Mediatora. Także dołączenie i odłączenie obiektu Colleague wymaga jedynie powiadomienia Mediatora, a nie wszystkich obiektów. Struktura ta jest przybliżoną analogią do sieci komputerowych, w których komputery znajdujące się w różnych podsieciach komunikują się za pośrednictwem routera. Poszczególne Komputery nie muszą znać adresów wszystkich innych komputerów na świecie, a jedynie adres najbli?szego routera.
Konsekwencje
Centralizacja mechanizmu komunikacji
- wyłączna odpowiedzialność obiektu Mediator
- zmiana mechanizmu wymaga tylko zmiany Mediatora
- prostota komunikacji vs. złożoność Mediatora
Niezależność obiektów Colleague od siebie
Uproszczenie protokołów obiektowych
- Zamiana relacji wiele-wiele na relacje jeden-wiele
Mediator narzuca centralizację mechanizmu komunikacji. Odpowiedzialność za komunikację przejmuje w całości Mediator, co z jednej strony pozwala w łatwy sposób modyfikować go lub wymieniać, z drugiej jednak powoduje znaczny wzrost złożoności tego obiektu. Wydaje się jednak, że zamiana taka jest opłacalna, poniewa? uwalnia od problemów związanych z komunikacją resztę obiektów w systemie. Drugą ważną zaletą wzorca jest uniezależnienie obiektów Colleague od siebie: nie posiadają one o sobie żadnej wiedzy, co pozwala modyfikować ich liczbę i funkcjonalność.
Mediator - przykład:
Prostym przykładem wzorca Mediator może być znany problem producentów i konsumentów. Producenci nie muszą posiadać jakiejkolwiek wiedzy o konsumentach, ponieważ ich zadaniem jest tylko zapełnianie bufora. Podobnie, konsumenci w żaden sposób nie zale?ą od producentów, a jedynie od bufora. Bufor pełni rolę mediatora, który koordynuje komunikację między dwoma typami obiektów. Dzięki zastosowaniu wzorca Mediator mo?liwe jest zwiększanie lub zmniejszanie liczby producentów i konsumentów bez zmiany struktury systemu.
public class Mediator { private boolean slotFull = false; private int number; public synchronized void storeMessage(int num) { while (slotFull) { try { wait(); } catch (InterruptedException ex ) { } } number = num; slotFull = true; notifyAll(); } public synchronized int retrieveMessage() { while (slotFull) { try { wait(); } catch (InterruptedException ex ) { } } notifyAll(); slotFull = false; return number; } }
Poniższy kod przedstawia klasę producenta. Jej logika jest zawarta w metodzie run(), która próbuje wstawić do bufora kolejną liczbę.
public class Producer extends Thread { private Mediator m; private int no; private static int count = 1; public Producer(Mediator m) { mediator = m; no = count++; } public void run() { int num; while (true) { m.storeMessage(num = (int)(Math.random()*100)); System.out.print("p" + no + "-" + num + " "); } } }
Klasa konsumenta, której kod został przedstawiony na slajdzie, próbuje w nieskończonej pętli odczytać wartość liczby przechowywanej w buforze Mediatora.
public class Consumer extends Thread { private Mediator m; private int no; private static int count = 1; public Consumer(Mediator m) { count = m; id = count++; } public void run() { while (true) { System.out.print("c" + no + "-" + m.retrieveMessage()); } } }