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:
- Mediator —Definuje komunikaci mezi objekty Colleague.
- Concrete Mediator — Implementuje komunikaci mezi objekty Colleague.
- Colleague —Komunikuje pouze s Mediatorem.
- 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.
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.