Моделът на посредника в Java

1. Общ преглед

В тази статия ще разгледаме модела на медиатора, един от моделите на поведение на GoF . Ще опишем целта му и ще обясним кога трябва да го използваме.

Както обикновено, ще предоставим и прост пример за код.

2. Модел на посредник

При обектно-ориентираното програмиране винаги трябва да се опитваме да проектираме системата по такъв начин, че компонентите да са свободно свързани и да се използват многократно . Този подход прави нашия код по-лесен за поддръжка и тестване.

В реалния живот обаче често ни се налага да се справяме със сложен набор от зависими обекти. Това е моментът, в който шаблонът на медиатора може да ви бъде полезен.

Целта на модела на посредника е да намали сложността и зависимостите между тясно свързани обекти, комуникиращи директно един с друг . Това се постига чрез създаване на обект-посредник, който се грижи за взаимодействието между зависимите обекти. Следователно цялата комуникация преминава през медиатора.

Това насърчава разхлабеното свързване, тъй като набор от компоненти, работещи заедно, вече не трябва да взаимодействат директно. Вместо това те се отнасят само до единичния обект на посредник. По този начин е по-лесно да използвате повторно тези обекти в други части на системата.

3. UML диаграма на модела на посредник

Нека сега разгледаме модела визуално:

В горната UML диаграма можем да идентифицираме следните участници:

  • Посредникът определя интерфейса, който обектите на Колега използват за комуникация
  • Колега дефинира абстрактния клас, съдържащ еднократна препратка към Посредника
  • ConcreteMediator капсулира логиката на взаимодействие между обекти на Колега
  • ConcreteColleague1 и ConcreteColleague2 комуникират само чрез Посредника

Както виждаме, обектите на Колега не се отнасят директно един към друг. Вместо това цялата комуникация се осъществява от Посредника .

Следователно, ConcreteColleague1 и ConcreteColleague2 могат да бъдат използвани по-лесно повторно.

Също така, в случай че трябва да променим начина, по който обектите на Колега работят заедно, трябва само да изменим логиката ConcreteMediator . Или можем да създадем ново изпълнение на Посредника.

4. Внедряване на Java

Сега, когато имаме ясна представа за теорията, нека разгледаме един пример, за да разберем по-добре концепцията на практика.

4.1. Примерен сценарий

Представете си, че изграждаме проста система за охлаждане, която се състои от вентилатор, захранване и бутон. Натискането на бутона или ще включи, или ще изключи вентилатора. Преди да включим вентилатора, трябва да включим захранването. По същия начин трябва да изключим захранването веднага след изключването на вентилатора.

Нека сега да разгледаме примерното изпълнение:

public class Button { private Fan fan; // constructor, getters and setters public void press(){ if(fan.isOn()){ fan.turnOff(); } else { fan.turnOn(); } } }
public class Fan { private Button button; private PowerSupplier powerSupplier; private boolean isOn = false; // constructor, getters and setters public void turnOn() { powerSupplier.turnOn(); isOn = true; } public void turnOff() { isOn = false; powerSupplier.turnOff(); } }
public class PowerSupplier { public void turnOn() { // implementation } public void turnOff() { // implementation } }

След това нека тестваме функционалността:

@Test public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() { assertFalse(fan.isOn()); button.press(); assertTrue(fan.isOn()); button.press(); assertFalse(fan.isOn()); }

Изглежда всичко работи добре. Но забележете как класовете Button, Fan и PowerSupplier са тясно свързани . The Button работи директно върху вентилатора и вентилатора взаимодейства с двата бутона и PowerSupplier.

Трудно би било да се използва повторно класът Button в други модули. Освен това, ако трябва да добавим второ захранване в нашата система, тогава ще трябва да модифицираме логиката на класа Fan .

4.2. Добавяне на образец на посредник

Сега, нека приложим шаблона на посредника, за да намалим зависимостите между нашите класове и да направим кода по-многократен.

Първо, нека представим класа Mediator :

public class Mediator { private Button button; private Fan fan; private PowerSupplier powerSupplier; // constructor, getters and setters public void press() { if (fan.isOn()) { fan.turnOff(); } else { fan.turnOn(); } } public void start() { powerSupplier.turnOn(); } public void stop() { powerSupplier.turnOff(); } }

След това нека модифицираме останалите класове:

public class Button { private Mediator mediator; // constructor, getters and setters public void press() { mediator.press(); } }
public class Fan { private Mediator mediator; private boolean isOn = false; // constructor, getters and setters public void turnOn() { mediator.start(); isOn = true; } public void turnOff() { isOn = false; mediator.stop(); } }

Отново нека тестваме функционалността:

@Test public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() { assertFalse(fan.isOn()); button.press(); assertTrue(fan.isOn()); button.press(); assertFalse(fan.isOn()); }

Нашата охладителна система работи както се очаква.

Сега, след като внедрихме модела на посредника, никой от класовете Button , Fan или PowerSupplier не комуникира директно . Те имат само една препратка към Посредника.

Ако трябва да добавим второ захранване в бъдеще, всичко, което трябва да направим, е да актуализираме логиката на посредника ; Класовете за бутони и вентилатори остават недокоснати.

Този пример показва колко лесно можем да отделим зависими обекти и да улесним поддръжката на нашата система.

5. Кога да използвате модела на медиатора

The Mediator Pattern is a good choice if we have to deal with a set of objects that are tightly coupled and hard to maintain. This way we can reduce the dependencies between objects and decrease the overall complexity.

Additionally, by using the mediator object, we extract the communication logic to the single component, therefore we follow the Single Responsibility Principle. Furthermore, we can introduce new mediators with no need to change the remaining parts of the system. Hence, we follow the Open-Closed Principle.

Sometimes, however, we may have too many tightly coupled objects due to the faulty design of the system. If this is a case, we should not apply the Mediator Pattern. Instead, we should take one step back and rethink the way we've modeled our classes.

Както при всички други модели, трябва да разгледаме конкретния ни случай на употреба, преди да приложим сляпо модела на посредник .

6. Заключение

В тази статия научихме за модела на посредника. Обяснихме какъв проблем решава този модел и кога всъщност трябва да обмислим използването му. Също така внедрихме прост пример за модела на дизайна.

Както винаги, пълните примерни кодове са достъпни в GitHub.