Purpose of the pattern
Mediator is made for 1 thing only: Reducing Chaos between dependencies. Objects, instead of communicating with each other directly, will communicate through the Mediator.
What do we get from that?
- Less coupled code: classes don’t use multiple external dependencies.
- Reusability: classes don’t depend on each other and can be reused in other projects and places.
- Single Responsibility: communication responsibility is moved to the Mediator, making it easier to maintain and analyze.
- Open/Closed: introducing new Mediators doesn’t require changing any code in the existing codebase.
The biggest downside? Mediator might become a large monolith object with thousands of lines.
Implementation
The diagram above Mediator
is an interface because we don’t want Colleague to depend on concrete classes but on abstraction.
MediatorImpl
is a concrete class that depends on all the Colleague
concrete classes. We’re cleaning our codebase, but at the same time, we introduce very dirty MediatorImpl
with multiple dependencies.
- Identify a group of tightly coupled classes that could be reused or are too dependent on each other, making your system too complex.
- Declare
Mediator
interface. - Create a concrete
Mediator
class that includes all the necessary dependencies. - Add
Mediator
interface reference to the group of objects and remove previous dependencies. - Change the code so that it calls
Mediator
instead of other classes.
Example
Your task is to make a Chat app with a group system. Each Chatter
is in a group, can send and receive messages from other Chatter
s. We’ll use Mediator
to control messages sent to groups by Chatters
. Here’s how we’ll structure our code:
Job Offers
Note that ChatMediator
has a join
method that needs to be called by the Chatters
to receive messages.
Let’s start by adding Group
:
enum class Group {
SPORT,
STUDENT;
}
For simplicity, we’ll only have 2 groups. Now, let’s define ChatMediator
interface so we can create Chatters
:
interface ChatMediator {
fun send(message: String, group: Group)
fun join(chatter: Chatter)
}
Now, let’s add Chatter
along with SportsChatter
and UniversityChatter
:
interface Chatter {
fun send(message: String, group: Group)
fun receive(message: String)
val groups: Set<Group>
}
class SportsChatter(private val mediator: ChatMediator) : Chatter {
init {
mediator.join(this)
}
override fun send(message: String, group: Group) {
mediator.send(message, group)
}
override fun receive(message: String) {
print("Sports received $message")
}
override val groups: Set<Group>
get() = setOf(Group.SPORT)
}
// For simplicity this class is almost identical to SportsChatter
class UniversityChatter(private val mediator: ChatMediator) : Chatter {
init {
mediator.join(this)
}
override fun send(message: String, group: Group) {
mediator.send(message, group)
}
override fun receive(message: String) {
print("University received $message")
}
// Note that we have 2 Groups here!!
override val groups: Set<Group>
get() = setOf(Group.SPORT, Group.STUDENT)
}
Please note that UniversityChatter
belongs to 2 groups Group.SPORT
and Group.STUDENT
. Finally, let’s implement GroupedChatMediator
:
class GroupedChatMediator : ChatMediator {
private val chatters = mutableListOf<Chatter>()
override fun send(message: String, group: Group) {
chatters.forEach { chatter ->
if (chatter.groups.contains(group)) {
chatter.receive(message)
}
}
}
override fun join(chatter: Chatter) {
chatters.add(chatter)
}
}
Here’s how to use it:
fun main() {
val chatMediator: ChatMediator = GroupedChatMediator()
val sportsChatter: Chatter = SportsChatter(chatMediator)
val universityChatter: Chatter = UniversityChatter(chatMediator)
universityChatter.send("Student ", Group.STUDENT) // University
sportsChatter.send("Sport ", Group.SPORT) // University + Sports
// We can add in more Chatters
val sportsChatter2: Chatter = SportsChatter(chatMediator)
sportsChatter2.send("Sport ", Group.SPORT) // University + Sports + Sports
}
And that’s it Mediator
is implemented! Well done.
Thanks for reading! Please clap if you learned something, and follow me for more!
Learn more about design patterns:
Based on the book:
“Wzorce projektowe : elementy oprogramowania obiektowego wielokrotnego użytku” — Erich Gamma Autor; Janusz Jabłonowski (Translator); Grady Booch (Introduction author); Richard Helm (Author); Ralph Johnson (Author); John M Vlissides (Author)
This article is previously published on proandroiddev.com