Mediator pattern in Java and Python Development 17.02.2017

Behavioral Design Patterns provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily. Following design patterns come under this category.

Mediator pattern adds a third party object (called mediator) to control the interaction between two objects (called colleagues). It helps reduce the coupling between the classes communicating with each other.

Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

Mediator design pattern is used to provide a centralized communication medium between different objects in a system. Rather than interacting directly with each other, objects ask the Mediator to interact on their behalf which results in reusability and loose coupling. It encapsulates the interaction between the objects and makes them independent from each other. This allows them to vary their interaction with other objects in a totally different way by implementing a different mediator.

The Mediator helps to reduce the complexity of the classes. Each object no longer has to know in detail about how to interact with the other objects.

When to use the Mediator Pattern

  • 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 sub-classing
dp_mediator.gif

The participants classes in this pattern are:

  • Mediator defines an interface for communicating with Colleague objects.
  • ConcreteMediator knows the Colleague classes and keep a reference to the Colleague objects. Implements the communication and transfer the messages between the Colleague classes.
  • Colleague classes keep a reference to its Mediator object. Communicates with the Mediator whenever it would have otherwise communicated with another Colleague.

For our example, we will try to implement a chat application where users can do group chat. Every user will be identified by it’s name and they can send and receive messages. The message sent by any user should be received by all the other users in the group.

Java

First of all we will create Mediator interface that will define the contract for concrete mediators.

File ChatMediator.java.

public interface ChatMediator {
    public void sendMessage(String msg, User user);
    void addUser(User user);
}

Users can send and receive messages, so we can have User interface or abstract class. I am creating User as abstract class like below.

File User.java

public abstract class User {
    protected ChatMediator mediator;
    protected String name;

    public User(ChatMediator med, String name){
        this.mediator = med;
        this.name = name;
    }

    public abstract void send(String msg);
    public abstract void receive(String msg);
}

Notice that User has a reference to the mediator object, it’s required for the communication between different users.

Now we will create ConcreteMediator class, it will have a list of users in the group and provide logic for the communication between the users.

File ChatMediatorImpl.java.

public class ChatMediatorImpl implements ChatMediator {
    private List<User> users;

    public ChatMediatorImpl(){
        this.users = new ArrayList<>();
    }

    @Override
    public void addUser(User user){
        this.users.add(user);
    }

    @Override
    public void sendMessage(String msg, User user) {
        for(User u : this.users){
            //message should not be received by the user sending it
            if(u != user){
                u.receive(msg);
            }
        }
    }
}

Now we can create concrete User classes to be used by client system.

File UserImpl.java

public class UserImpl extends User {
    public UserImpl(ChatMediator med, String name) {
        super(med, name);
    }

    @Override
    public void send(String msg){
        System.out.println(this.name+": Sending Message: "+msg);
        mediator.sendMessage(msg, this);
    }
    @Override
    public void receive(String msg) {
        System.out.println(this.name+": Received Message: "+msg);
    }
}

Notice that send() method is using mediator to send the message to the users and it has no idea how it will be handled by the mediator.

Let’s test this our chat application with a simple program where we will create mediator and add users to the group and one of the user will send a message.

File ChatClient.java.

public class ChatClient {
    public static void main(String[] args) {
        ChatMediator mediator = new ChatMediatorImpl();
        User user1 = new UserImpl(mediator, "John");
        User user2 = new UserImpl(mediator, "Lisa");
        User user3 = new UserImpl(mediator, "Maria");
        User user4 = new UserImpl(mediator, "David");
        mediator.addUser(user1);
        mediator.addUser(user2);
        mediator.addUser(user3);
        mediator.addUser(user4);

        user1.send("Hi All");
    }
}

Notice that client program is very simple and it has no idea how the message is getting handled and if mediator is getting user or not.

Python 3

import abc

class User(metaclass=abc.ABCMeta):
    def __init__(self, med, name):
        self.mediator = med
        self.name = name

    @abc.abstractmethod
    def send(self, msg):
        pass

    @abc.abstractmethod
    def receive(self, msg):
        pass


class ChatMediatorImpl:
    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 UserImpl(User):
    def send(self, msg):
        print(self.name + ": Sending Message: " + msg)
        self.mediator.send_message(msg, self)

    def receive(self, msg):
        print(self.name + ": Received Message: " + msg)


if __name__ == '__main__':
    mediator = ChatMediatorImpl()
    user1 = UserImpl(mediator, "John")
    user2 = UserImpl(mediator, "Lisa")
    user3 = UserImpl(mediator, "Maria")
    user4 = UserImpl(mediator, "David")
    mediator.add_user(user1)
    mediator.add_user(user2)
    mediator.add_user(user3)
    mediator.add_user(user4)

    user1.send("Hi All")