From a403c84add714a87071590800f18dc182b7cc45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Mon, 21 Jun 2021 21:33:41 +0300 Subject: [PATCH] Add explanation for mediator pattern --- mediator/README.md | 185 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 7 deletions(-) diff --git a/mediator/README.md b/mediator/README.md index edb748a8f..627de4e28 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -11,21 +11,192 @@ tags: --- ## 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. + +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. + +## Explanation + +Real-world example + +> Rogue, wizard, hobbit, and hunter have decided to join their forces and travel in the same +> party. To avoid coupling each member with each other, they use the party interface to +> communicate with each other. + +In plain words + +> Mediator decouples a set of classes by forcing their communications flow through a mediating +> object. + +Wikipedia says + +> In software engineering, the mediator pattern defines an object that encapsulates how a set of +> objects interact. This pattern is considered to be a behavioral pattern due to the way it can +> alter the program's running behavior. In object-oriented programming, programs often consist of +> many classes. Business logic and computation are distributed among these classes. However, as +> more classes are added to a program, especially during maintenance and/or refactoring, the +> problem of communication between these classes may become more complex. This makes the program +> harder to read and maintain. Furthermore, it can become difficult to change the program, since +> any change may affect code in several other classes. With the mediator pattern, communication +> between objects is encapsulated within a mediator object. Objects no longer communicate directly +> with each other, but instead communicate through the mediator. This reduces the dependencies +> between communicating objects, thereby reducing coupling. + +**Programmatic Example** + +In this example, the mediator encapsulates how a set of objects interact. Instead of referring to +each other directly they use the mediator interface. + +The party members `Rogue`, `Wizard`, `Hobbit`, and `Hunter` all inherit from the `PartyMemberBase` +implementing the `PartyMember` interface. + +```java +public interface PartyMember { + + void joinedParty(Party party); + + void partyAction(Action action); + + void act(Action action); +} + +@Slf4j +public abstract class PartyMemberBase implements PartyMember { + + protected Party party; + + @Override + public void joinedParty(Party party) { + LOGGER.info("{} joins the party", this); + this.party = party; + } + + @Override + public void partyAction(Action action) { + LOGGER.info("{} {}", this, action.getDescription()); + } + + @Override + public void act(Action action) { + if (party != null) { + LOGGER.info("{} {}", this, action); + party.act(this, action); + } + } + + @Override + public abstract String toString(); +} + +public class Rogue extends PartyMemberBase { + + @Override + public String toString() { + return "Rogue"; + } +} + +// Wizard, Hobbit, and Hunter are implemented similarly +``` + +Our mediator system consists of `Party` interface and its implementation. + +```java +public interface Party { + + void addMember(PartyMember member); + + void act(PartyMember actor, Action action); +} + +public class PartyImpl implements Party { + + private final List members; + + public PartyImpl() { + members = new ArrayList<>(); + } + + @Override + public void act(PartyMember actor, Action action) { + for (var member : members) { + if (!member.equals(actor)) { + member.partyAction(action); + } + } + } + + @Override + public void addMember(PartyMember member) { + members.add(member); + member.joinedParty(this); + } +} +``` + +Here's a demo showing the mediator pattern in action. + +```java + // create party and members + Party party = new PartyImpl(); + var hobbit = new Hobbit(); + var wizard = new Wizard(); + var rogue = new Rogue(); + var hunter = new Hunter(); + + // add party members + party.addMember(hobbit); + party.addMember(wizard); + party.addMember(rogue); + party.addMember(hunter); + + // perform actions -> the other party members + // are notified by the party + hobbit.act(Action.ENEMY); + wizard.act(Action.TALE); + rogue.act(Action.GOLD); + hunter.act(Action.HUNT); +``` + +Here's the console output from running the example. + +``` +Hobbit joins the party +Wizard joins the party +Rogue joins the party +Hunter joins the party +Hobbit spotted enemies +Wizard runs for cover +Rogue runs for cover +Hunter runs for cover +Wizard tells a tale +Hobbit comes to listen +Rogue comes to listen +Hunter comes to listen +Rogue found gold +Hobbit takes his share of the gold +Wizard takes his share of the gold +Hunter takes his share of the gold +Hunter hunted a rabbit +Hobbit arrives for dinner +Wizard arrives for dinner +Rogue arrives for dinner +``` ## Class diagram + ![alt text](./etc/mediator_1.png "Mediator") ## Applicability + Use the Mediator pattern when -* a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand -* reusing an object is difficult because it refers to and communicates with many other objects -* a behavior that's distributed between several classes should be customizable without a lot of subclassing +* A set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand +* Reusing an object is difficult because it refers to and communicates with many other objects +* A behavior that's distributed between several classes should be customizable without a lot of subclassing -## Real world examples +## Known uses * All scheduleXXX() methods of [java.util.Timer](http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) * [java.util.concurrent.Executor#execute()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-)