Hlavní navigace

Složitý graf závislostí zjednodušen pomocí návrhového vzoru Mediator

 Autor: Macrovector
Zjednodušte graf závislostí objektů pomocí návrhového vzoru Mediator. V českých luzích je také znám jako Prostředník. V jednoduchém tutoriálu si ukážeme, jak takový Mediator implementovat v jazyce C#.
Daniel Rusnok 22. 4. 2020
Doba čtení: 4 minuty

Sdílet

Předpoklady

Tutoriál očekává od čtenáře základní znalosti jazyka C# a objektově orientovaného programování.

Kde se tu vzal a co tu chce?

Návrhový vzor Mediator byl poprvé oficiálně představen v knize Design Patterns: Elements of Reusable Object-Oriented Software.


Upřímně jsem tuto knihu nikdy nečetl, jelikož internet je plný výtažků z této knihy a nejsem fanouškem učení se z knih.

Mediator zapouzdřuje vzájemnou interakci a komunikaci objektů. Jedná se o návrhový vzor podporující snížení referencí objektů a vyhnutí se složitého grafu závislostí.

Složitý graf závislostí je něco, čemu se snažíme jako programátoři vyhnout a návrhový vzor Mediator je konkrétní implementace jak tomu předejít. Mediator umožňuje objektům vyhnout se přímé závislosti na sobě a zapouzdřuje komunikaci jako centrální komunikační objekt.

Pro komunikaci si vytvoříme centrální objekt Mediator. Tento jediný objekt bude mít veškerou zodpovědnost za udržování referencí na objekty v našem projektu. Bude také zodpovědný za předávání veškerých zpráv či komunikaci mezi a k objektům. Můžete si ho představit jako komunikační uzel.

Vztah mezi komponentami

Mediator se obecně dělí na čtyři komponenty:

  1. Mediator  —Definuje komunikaci mezi objekty Colleague.
  2. Concrete Mediator  — Implementuje komunikaci mezi objekty Colleague.
  3. Colleague —Komunikuje pouze s Mediatorem.
  4. Concrete Colleague — Přijímá zprávy od Mediatoru.

Mediator je běžné definován jako abstraktní třída. Colleague je také abstraktní třída, ale zato reprezentuje příbuznou kolekci objektů. Je závislá pouze na Mediatoru a komunikuje pouze s ním. Vztah mezi abstraktními třídami je obousměrný.

Concrete Mediator dědí z abstraktního Mediatoru a implementuje komunikaci, která byla definována abstraktní metodou Send v Mediatoru bázovém.

Concrete Colleague je jednoduše řečeno různý typ potomků, který dědí z bázové třídy Colleage a definuje specifické chování.

Implementace

Pojďme implementovat náš první Mediator. Nejprve potřebujeme vytvořit abstraktní třídu s názvem Mediator a definici abstraktní metody Send.

public abstract class Mediator

{

  public abstract void Send(string message, Colleague colleague);

}

Poté vytvoříme abstraktní třídu Colleague.

public abstract class Colleague

{

  protected Mediator _mediator;



  protected Colleague(Mediator mediator){

    _mediator = mediator;

  }



  public void Send(string message){

    _mediator.Send(message, this);

  }



  public abstract void HandleNotification(string message);

}

Jak můžete vidět, přidal jsem trochu více kódu, který záhy vysvětlím.

Nejprve jsem přidal konstruktor, který přijímá parametr Mediator a ukládá ho do dědičné třídní proměnné.


Poté jsem implementoval metodu Send, která bude zasílat zprávy skrze Mediator, který je uložen v třídní proměnné.


A nakonec ještě definuji abstraktní metodu HandleNotification. Tato metoda slouží jako předpis místa, kde budeme přijímat zprávy.


Nyní pojďme přidat třídy reprezentující komponentu Concrete Colleague. Implementujeme je tak, aby dědily z abstraktní bázové třídy Colleague.

public class Colleague1 : Colleague

{

  public Colleague1(Mediator mediator) : base(mediator) { }



  public override void HandleNotification(string message){

    Console.WriteLine($"Colleague1 receives message:{message}");

  }

}



public class Colleague2 : Colleague

{

  public Colleague1(Mediator mediator) : base(mediator) { }



  public override void HandleNotification(string message){

    Console.WriteLine($"Colleague2 receives message:{message}");

  }

}

Pokud dědíme z třídy Colleague, musíme implementovat tělo předepsané abstraktní třídy HandleNotification. Implementace je jednoduchý informativní zápis do konzole o tom, že zpráva dorazila v pořádku.

Ještě potřebujeme vytvořit poslední třídu, která bude reprezentovat komponentu Concrete Mediator a tak ji i nazveme.

public class ConcreteMediator : Mediator

{

  public Colleague1 Colleague1 { get; set; }

  public Colleague2 Colleague2 { get; set; }



  public override void Send(string message, Colleague colleague){

    if(colleague == Colleague1){

      Colleague2.HandleNotification(message);

    }

    else{

      Colleague1.HandleNotification(message);

    }

  }

}

Podívejte se na metodu Send. V ní vidíte implementaci funkce pro kterou nám byl návrhový vzor vytvořen, a to zajištění komunikace mezi objekty. V této konkrétní situaci zasílám zprávy objektu Colleague1 do objektu Colleague2 a naopak.

Dost bylo definování tříd. Nyní pojďme implementovat program, který Mediator využije.

class Program

{

  static void Main(string[] args){

    var mediator = new ConcreteMediator();

    var c1 = new Colleague1(mediator);

    var c2 = new Colleague2(mediator);



    mediator.Colleague1 = c1;

    mediator.Colleague2 = c2;



    c1.Send("Hello, World! (from c1)");

    c2.Send("Hello, World! (from c2)");

  }

}

V programu jsem vytvořil objekt typu ConcreteMediator a následně objekty typu Colleague1 a Colleague2. Všimněte si, že oběma předávám objekt Mediator jako parametr jejich konstruktoru.

Poté informujeme Mediator předáním referencí na objekty c1 a c2 do předem připravených Properties. Tím jsme zajistili obousměrnou komunikaci a jsme připraveni k zasílání zpráv.

Po spuštění programu můžeme vidět výpis v konzoli, kde se můžete přesvědčit, že zprávy dorazily v pořádku.


Výhody použití Mediatoru

Mediator si můžete představit jako komunikační uzel. Stará se o správný směr zasílání zpráv za vás. Taková zpráva nemusí být pouze řetězcového typu.

MIF obecny

Benefitem je také schopnost zapouzdřovat interakci mezi objekty tak, aby objekty o sobě nemusely vůbec vědět.

Návrhové vzory jsou pouze jedna z možností, jak řešit obecné problémy. Nejedná se o nařízení vrytá do kamene. Existuje mnoho různých implementací. Tímto velmi jednoduchým tutoriálem jsem vám chtěl nastínit, jak by mohla implementace vypadat.