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());
    }
  }
}