Mediator design pattern in python

This is one of the best methods among all design patterns and is a variation of behavioral design patterns. In software engineering, our software is composed of different objects, and the number of objects varies from time to time. When introducing a new feature, we need to add more objects to our project, but there is a way of interacting with objects and specific interaction rules. When a new object is introduced, we need to manipulate the way interaction is In both the objects, and this cycle continues. And it is a daunting task because we are supposed to change attributes when a new object comes. If we modify one object, we have to modify all associated objects, which develops some dependencies. So this problem needs to be solved, and there should be some way through which, even after the introduction of the new object, the interaction would be normal, and all objects can interact easily. This problem can be solved using a mediator design pattern.

The mediator design pattern uses encapsulation, encapsulating all the object's information and then a simple interface is implemented to deal with the communication of objects. This interface is used as a communication mode, meaning objects can communicate through this interface. Here objects are unaware of another object, and they take the mediator's help, the interface. If the objects start communicating directly, it has many drawbacks, such as introducing tight coupling, which will lead to high maintenance costs, and the components will develop dependencies among themselves. The mediator design pattern offers loose coupling, and all drawbacks of tight coupling vanish. The communication complexity among objects is reduced by using this design pattern. The mediator layer is easy to maintain, and the cost is also low.

Elements of mediator design patterns -

  • Mediator - this interface will help the objects in their communication. This layer acts as a helper. The encapsulated information of all objects makes interaction simple and reduces communication complexity. Here loose coupling is used.
  • Participant or entity: this is the main consumer who can come and leave the system anytime, and this uncertainty is the reason behind the introduction of a new layer for communication; hence the central data is not directly accessed.
  • ParticipantReference: this acts as a helper in communication. It transfers the messages and distributes the messages among all the necessary participants.

Problem statement :

Suppose you are working on registration. It might be filled from an app or some specific website. So there must be a profile of users, and there will be particular attributes such as name, age, designation, interests, etc.

So it would be best if you had a dialogue box to edit user profiles or their attributes. The dialogue box has many fields of text, buttons, colors, and the headline and checkbox; as there are many objects, they must be handled and accessed wisely because they can develop dependencies. While filling the form, users can go offline, or they can leave filling the form and they can come again to fill it. The main case is supposed there is no mediator, so while the user writes, it will create objects, and the user might leave before filling the whole form, so even if the user did not submit the form, it made changes in the object. Another example is related to the check box, the data must be validated after clicking the box, and if the data is valid, only it can be submitted. There might be issues with having this logic implemented directly inside the code of the form elements. You make these elements' classes much harder to reuse in other app forms. The tight coupling will introduce various problems, so how can we solve this problem? To deliver good software, we need to implement some reliable solution.

Merge Sort

Solution statement :

 In the above problem, the main issue is direct communication, so using a mediator design pattern is the best option to reduce the complexity. In the editor design pattern, the objects can collaborate in all directions without developing dependencies. They can communicate independently of each other. Here calling the special mediator objects will lead to communication with the appropriate object. As a result, the object will be dependent only on a single class rather than associating with every object. The mediator design pattern uses encapsulation, encapsulating all the object's information, and then a simple interface is implemented to deal with the communication of objects. This interface is used as a communication mode, meaning objects can communicate through this interface.

So in the above problem, the dialogue box can be used as a mediator if encapsulation is done so that the dialogue box will be aware of all the information, so it does not require adding objects for interaction. The mediator can work as a router between objects.

Let's talk about the checkbox problem, so previously it needed to verify all the data. Then only the submission can work, but in the mediator design pattern, the change in the actual object is more helpful. Now, the checkbox does not require validation of the data. After getting clicked, it will send the information to the dialogue box that the data must need to be validated. Upon request, the dialogue box will validate all the data, which is done by passing values to different individual objects. Hence the mediator will help in validation. We do not need to create any extra objects.

The coupling can be loosed, and this can be done by implementing the common interface between all the objects. Then any object can work with any existing object without extra tasks or restrictions. Even if the relationship is complex, it can be easily encapsulated. The number of objects is reduced as the single mediator holds many relations.

A real-life example of a mediator design pattern

The air traffic department has all the information that a pilot needs for a safe flight and landing. You all must know the complexity of airports and air traffic, so how a pilot gets information about where to land, how to land, which plane is coming from which direction, and all? Do they contact all other stations and pilots to get aware of all these kinds of stuff? So the answer is complete no because pilots have many more problems during flight, so if they start communicating with everyone individually, then the flight will not be possible, and they might even crash. So they get all the information from air traffic. The air traffic controller does not control all the flights. They just enforce all necessary constraints in the terminal. Here air traffic department works as a mediator.

Example.py

from abc import ABC, abstract method
class User_1():
    def __init__(self, med, name_):
        self.mediator = med
        self.name = name_


    @abstractmethod
    def send(self, msg):
        pass
    @abstractmethod
    def receive(self, msg):
        pass
class Chat_Mediator:
    def __init__(self):
        self.users = []
    def add_user(self, user):
        self.users.append(user)
    def send_message(self, msg, user):
        for u in self.users:
            if u != user:
                u.receive(msg)
class User_1(User_1):
    def send(self, msg):
        print(self.name + ": we are providing information : " + msg)
        self.mediator.send_message(msg, self)
    def receive(self, msg):
        print(self.name + ": Received Message from Ms sweta: " + msg)
if __name__ == '__main__':
    mediator_1 = Chat_Mediator()
    consumer1 = User_1(mediator_1, "Sweta")
    consumer2 = User_1(mediator_1, "Aditya")
    consumer3 = User_1(mediator_1, "Rakhi")
    consumer4 = User_1(mediator_1, "Mishri")
    mediator_1.add_user( consumer1)
    mediator_1.add_user( consumer2)
    mediator_1.add_user( consumer3)
    mediator_1.add_user( consumer4)
    consumer1.send("Hello, every one is invited and we feel immense pleasure to accompanied by you .")

Output :

Sweta: we are providing information: Hello, every one is invited and we feel immense pleasure to be accompanied by you .
Aditya: Received Message from Ms Sweta : Hello, every one is invited and we feel immense pleasure to be accompanied by you .
Rakhi: Received Message from Ms Sweta : Hello, every one is invited and we feel immense pleasure to be accompanied by you .
Mishri: Received Message from Ms Sweta : Hello, every one is invited and we feel immense pleasure to be accompanied by you .

Explanation :

This is an example of a chat system. We all are aware of chats as we use social media often; nowadays, it's part of our daily life. So you must know about groups, what happens to the group? When a sender sends something in the group, does he send it to everybody individually, or when he sends it in the group, is it received by all group members without sending them individually? The answer is that all group members receive the message, and there is no need to send it individually. It makes our work simple. Just by sending in a group, the problem is solved but think once how this happens and how it is sent to everybody because the sender is not interacting with everyone directly still the communication is happening, so its the mediation which develops an interface and by that interface the message is delivered to all desired locations/user. So here mediator plays a major role. Here in the example, the same thing is done. A single user sends the message but is received by all existing users and implements a group structure.

User class

class User_1():
    def __init__(self, med, name_):
        self.mediator = med
        self.name = name_

 This is our user class, which consists of attributes such as name and mediator. In order to create any valid user, we must pass the mediator's name and the user's name as an argument. Then the valid user will be created using the above class.

Mediator :

class Chat_Mediator:
    def __init__(self):
        self.users = []
    def add_user(self, user):
        self.users.append(user)
    def send_message(self, msg, user):
        for u in self.users:
            if u != user:
                u.receive(msg)

So this will act as an interface and uses encapsulation, encapsulating all the object's information, and then a simple interface is implemented to deal with the communication of objects. This interface is used as a communication mode, meaning objects can communicate through this interface. As per the code, you can see we are appending users, so all the required users will be appended and receive the message. The send message class is also defined here and is responsible for sending messages. This function takes the user and the message as an argument, so it will deliver the messages to respective users whenever it is called. If there are multiple users, then we use the loop. It minimizes our code complexity as well as code length and works efficiently, and we can add some conditional checks if needed. As you can see, we have defined one conditional check, so this is how the mediator is implemented and working. We need to call send user function, which will send a message to all existing objects as shown in the code.

In the output, you can see how there is one sender, and the rest are receivers. If we want to send more messages, we can call send function and implement a loop if there are multiple messages.

As you can see, without sending or interacting with individual objects, we can still access and use them. This is possible only because of the availability of a mediator. So this is how the mediator design pattern helps solve the interaction problem.

Advantages of mediator design pattern :

  • Here all the process is controlled by a single mediator, so it follows the single responsibility principle, which is very beneficial and efficient.
  • It always follows the open-close principle, so even if we do not make any changes in client-side code, we can introduce new objects and access them. New object creation will not disturb the basic structure of code.
  • We can use loose coupling, which can be done by implementing the common interface between all the objects. Then any object can work with any existing object without extra tasks or restrictions.

Disadvantages of mediator design pattern :

  • The centralization of power is sometimes inefficient because suppose if any bug is encountered in the mediator, the whole system cannot work. It will be shut down until the mediator is fixed.
  • The mediator can contain too much information, which can complex its structure. This property is also known as a god object.
  • The mediator objects may encounter various logic, which sometimes becomes hard to handle, so we must be careful about every single feature of the mediator. We must implement it keenly because any single fault in the system can mislead the software's functionality.