Медіатор (Mediator)

Я трохи думав над тим який має бути приклад для Медіатора і мені на думку нічого кращого ніж класичний приклад із взаємодією елементів в користувацькому інтерфейсі не спадало. Потім ще декілька варіантів прокрутилися, аж поки не спала на думку взаємодія нейронів. Я ще трохи подумав і в голову прийшла геніальна ідея (звичайно я не ручаюся, що більше такого прикладу немає, але мене просвітило ним). Наш мозок є медіатором до різних частин тіла. Мозок ідеально підходить під опис дизайн-патерну Медіатор.Просто спробуйте уявити, якби кожна із частин вашого тіла знала одна про іншу. Якщо б ваше око бачило щось приємне, то воно мало б знати як напряму зв’язатися із ногами і змусити їх рухатися у заданому напрямку. Або якщо б вас хтось вдарив в живіт, ваш живіт б мусів навчитися захищатися руками. Живіт може й боліти, тоді він буде змушений знати про цілу систему м’язів, щоб змусити тіло прийняти розслаблююче положення. Взаємодія описана вище, як багато-до-багатьох не є природньою для нашого тіла. Проте, чомусь, вона часто застосовна деякими програмістами до їхнього коду. Спочатку, поки програміст все пише по свіжому, такий код працює нормально, але із часом він перетворється на суцільний спагетті[1] безлад в якому розібратися важко, а змінити поведінку, не поломавши чогось, також складно.

Наше тіло має одну центральну систему, яка аналізує прийняті сигнали і здійснює потрібні реакції. Це можна застосувати і до коду, який ми пишемо.

Медіатор централізує взаємодію між компонентами, таким чином послаблюючи їхню зв’язність.[2]

Медіатор елегантно спрощує розуміння взаємодії між компонентами. Це полегшує підтримку коду у майбутньому, але, оскільки логіка централізована, вона може стати досить складною, зрештою чим наш мозок і є.

Нижче, наперед, наведемо консольний вивід, демонструючий приклад в дії:

Enter body part (‘Ear’, ‘Eye’, ‘Hand’ or empty to exit):

Ear

Enter what you hear:

You are cool!

FACE: Smiling...

Enter body part (‘Ear’, ‘Eye’, ‘Hand’ or empty to exit):

Hand

What you feel is soft? (Yes/No)

Yeah!

What you feel is hurting? (Yes/No)

No

LEG: Stepping forward...

HAND: Embracing what is in front of you...

Enter body part (‘Ear’, ‘Eye’, ‘Hand’ or empty to exit):

Ear

Enter what you hear:

You are dumbass stupid guy!

LEG: Stepping forward...

HAND: Just hit offender...

LEG: Just kicked offender in front of you...

Enter body part (‘Ear’, ‘Eye’, ‘Hand’ or empty to exit):

Hand

What you feel is soft? (Yes/No)

No

What you feel is hurting? (Yes/No)

Yes

LEG: Stepping back...

 

Як видно, мозок знає як діяти в різних ситуаціях і які частини тіла слід задіяти. То як це відбувається?

Мозок (або новоспечений Медітор) знає про кожну частину тіла (colleague), а кожна частина тіла знає про мозок, тому може передавати сигнали йому та приймати їх. Звичайно, кожна частина тіла виконує ще й свою безпосередню функціональність.

Уривок коду 17.1. Базовий клас для частин тіла сolleague (знає про мозок)

class BodyPart

{

    private readonly Brain _brain;

    public BodyPart(Brain brain)

    {

        _brain = brain;

    }

    public void Changed()

    {

        _brain.SomethingHappenedToBodyPart(this);

    }

}

 

Уривок коду 17.2. Конкретна реалізація colleague може виглядати так

class Ear : BodyPart

{

    private string _sounds = string.Empty;

    public Ear(Brain brain) : base(brain) { }

 

    public void HearSomething()

    {

        Console.WriteLine("Enter what you hear:");

        _sounds = Console.ReadLine();

 

        Changed();

    }

    public string GetSounds()

    {

        return _sounds;

    }

}

 

Як бачимо вухо (Ear) може чути (HearSomething) і може передати звуки на аналіз мозку (GetSounds). Деякі чатини тіла мають іншу функціональність. Як для прикладу реалізація класу обличчя (Face):

Уривок коду 17.3. Обличчя

class Face : BodyPart

{

    public Face(Brain brain)

        : base(brain)

    {

    }

    public void Smile()

    {

        Console.WriteLine("FACE: Smiling...");

    }

}

 

Як і слід було очікувати клас медіатора є досить громіздким оскільки він відповідає за «розрулювання» ситуації. Можливо вам не сподобається те як реалізовано цей конкретний мозок, але це не є аж на стільки важливо. Важливо зрозуміти як він діє.

Уривок коду 17.4. Мозок, або медіатор

// Медіатор

class Brain

{

    public Brain()

    {

        CreateBodyParts();

    }

 

    private void CreateBodyParts()

    {

        Ear = new Ear(this);

        Eye = new Eye(this);

        Face = new Face(this);

        Hand = new Hand(this);

        Leg = new Leg(this);

    }

 

    public Ear Ear { get; private set; }

    public Eye Eye { get; private set; }

    public Face Face { get; private set; }

    public Hand Hand { get; private set; }

    public Leg Leg { get; private set; }

 

    public void SomethingHappenedToBodyPart(BodyPart bodyPart)

    {

        if (bodyPart is Ear)

        {

            string heardSounds = ((Ear)bodyPart).GetSounds();

 

            if (heardSounds.Contains("stupid"))

            {

                // Атакуємо образника

                Leg.StepForward();

                Hand.HitPersonNearYou();

                Leg.Kick();

            }

            else if (heardSounds.Contains("cool"))

            {

                Face.Smile();

            }

        }

        else if (bodyPart is Eye)

        {

            // Мозок може проаналізувати, що ви бачите і

            // прореагувати відповідно, використовуючи різні частини тіла

        }

        else if (bodyPart is Hand)

        {

            var hand = (Hand)bodyPart;

 

            bool hurtingFeeling = hand.DoesItHurt();

            if (hurtingFeeling)

            {

                Leg.StepBack();

            }

 

            bool itIsNice = hand.IsItNice();

            if (itIsNice)

            {

                Leg.StepForward();

                Hand.Embrace();

            }

        }

        else if (bodyPart is Leg)

        {

            // Якщо на ногу впаде цегла міняємо вираз обличчя J

        }

    }

}

 

Додаю трохи UML:

UML-діаграма 9. Медіатор

[1] http://en.wikipedia.org/wiki/Spaghetti_code

[2] Mediator. Intent. Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

 Медіатор. Призначення. Визначає об’єкт, що інкапсулює взаємодію між множиною об’єктів. Медіатор покрацює слабкозв’язність шляхом утримання об’єктів від прямих посилан один на одного, а також дозволяє вам незалежно змінювати взаємодію.