Behavioral Design Pattern

Kotlin Design Patterns: Mediator

Michal Ankiersztajn
ProAndroidDev
Published in
3 min readApr 10, 2024

--

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

Mediator class diagram

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.

  1. Identify a group of tightly coupled classes that could be reused or are too dependent on each other, making your system too complex.
  2. Declare Mediator interface.
  3. Create a concrete Mediator class that includes all the necessary dependencies.
  4. Add Mediator interface reference to the group of objects and remove previous dependencies.
  5. 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:

Example class diagram

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:

Design Patterns In Kotlin

15 stories

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)

--

--