Gang of Four Behavioural pattern: Mediator
Behavioural Pattern
Defines simplified communication between classes. 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.
To get the code for this example:
git clone https://github.com/spotadev/gangoffour.git
In src/main/java navigate to this package:
com.javaspeak.designpatterns.go4.behavioural.mediator
You can run the code from the main method of:
MediatorApplication
In this example the ChatMediator mediates between the ChatUsers. Only the ChatMediator has a references to all consumers and producers. In this example the ChatUsers are both consumers and producers: they can both send and receive messages. A ChatUser sends and receives a message via the ChatMediator.
package com.javaspeak.designpatterns.go4.behavioural.mediator;
/**
* Text book description:
* <ul>
* Defines simplified communication between classes. 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.
* </ul>
* In this example the ChatMediator mediates between the ChatUsers. Only the ChatMediator has
* references to all consumers and producers. In this example the ChatUsers are both consumers
* and producers: they can both send and receive messages. A ChatUser sends and receives a message
* via the ChatMediator.
* <p>
* Before a ChatUser can send a message a group must exist. The ChatUser can check whether a group
* exists and if it does not create one by calling the createNewGroup(..) method on the
* ChatMediator. Once a group exists ChatUsers can subscribe to the group. ChatUsers can then send
* messages to the group by calling the sendMessage(..) method on the ChatMediator. The
* ChatMediator in return calls receiveMessage(..) on each of the ChatUsers that is subscribed to
* the group.
*
* @author John Dickerson - 21 Feb 2020
*/
public class MediatorApplication {
public void runExample() {
// The chatMediator to pass through the constructor of each ChatUser
ChatMediator chatMediator = new ChatMediatorImpl();
ChatUser chatUserOne = new ChatUserImpl( chatMediator, "chatUserOne" );
ChatUser chatUserTwo = new ChatUserImpl( chatMediator, "chatUserTwo" );
ChatUser chatUserThree = new ChatUserImpl( chatMediator, "chatUserThree" );
String[] groups = chatUserOne.getGroups();
String groupName = "kite surfing";
// ChatUserOne checks if the "kite surfing" group already exists
// and creates one if it does not exist
if ( !chatUserOne.groupExists( groups, groupName ) ) {
chatUserOne.createNewGroup( groupName );
}
// three ChatUsers subscribe to the "kite surfing" group
chatUserOne.subscribeToGroup( groupName );
chatUserTwo.subscribeToGroup( groupName );
chatUserThree.subscribeToGroup( groupName );
// ChatUserOne sends a message to the group
chatUserOne.sendMessage( groupName,
"Kite meet at Shoreham, Saturday. Any one want to go?" );
// ChatUserTwo sends a message to the group
chatUserTwo.sendMessage( groupName, "Yes I would like to go" );
// ChatUserThree sends a message to the group
chatUserThree.sendMessage( groupName, "Count me in as well" );
}
public static void main( String args[] ) {
MediatorApplication application = new MediatorApplication();
application.runExample();
}
}
package com.javaspeak.designpatterns.go4.behavioural.mediator;
/**
* This mediator is central to the Mediator pattern.
* <p>
* The ChatMediator mediates between the ChatUsers. Only the ChatMediator has a references to
* all consumers and producers. In this example the ChatUsers are both consumers and producers:
* they can both send and receive messages. A ChatUser sends and receives a message via the
* ChatMediator.
*
* @author John Dickerson - 21 Feb 2020
*/
public interface ChatMediator {
/**
* Returns all groups
*
* @return
* all groups
*/
public String[] getGroups();
/**
* Creates a new group
*
* @param groupName
* Group Name to create the group with
*
* @return
* true if the group was created. false indicates the group was not created because it
* exists already.
*/
public boolean createNewGroup( String groupName );
/**
* Subscribe to the group. If the group does not exist create it and subscribe
*
* @param groupName
* GroupName to subscribe to
*
* @param chatUser
* ChatUser which is subscribing
*/
public void subscribeToGroup( String groupName, ChatUser chatUser );
/**
* Send message to group
*
* @param group
* Group to send message to
*
* @param chatUser
* ChatUser which is sending the message
*
* @param message
* the message to send to the group
*/
public void sendMessage( String group, ChatUser chatUser, String message );
}
package com.javaspeak.designpatterns.go4.behavioural.mediator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
/**
* This mediator is central to the Mediator pattern.
* <p>
* The ChatMediator mediates between the ChatUsers. Only the ChatMediator has a references to all
* consumers and producers. In this example the ChatUsers are both consumers and producers: they
* can both send and receive messages. A ChatUser sends and receives a message via the
* ChatMediator.
*
* @author John Dickerson - 22 Feb 2020
*/
public class ChatMediatorImpl implements ChatMediator {
private Set<String> groups = new ConcurrentSkipListSet<String>();
private Map<String, Set<ChatUser>> chatUsersByGroup =
new ConcurrentHashMap<String, Set<ChatUser>>();
@Override
public String[] getGroups() {
return groups.toArray( new String[] {} );
}
@Override
public boolean createNewGroup( String groupName ) {
if ( groups.contains( groupName ) ) {
return false;
}
else {
groups.add( groupName );
return true;
}
}
@Override
public void subscribeToGroup( String group, ChatUser chatUser ) {
Set<ChatUser> groupChatUsers = chatUsersByGroup.get( group );
if ( groupChatUsers == null ) {
groupChatUsers = new ConcurrentSkipListSet<ChatUser>();
chatUsersByGroup.put( group, groupChatUsers );
}
groupChatUsers.add( chatUser );
}
@Override
public void sendMessage( String groupName, ChatUser chatUser, String message ) {
Set<ChatUser> groupChatUsers = chatUsersByGroup.get( groupName );
if ( groupChatUsers.contains( chatUser ) ) {
for ( ChatUser groupChatUser : groupChatUsers ) {
if ( !groupChatUser.equals( chatUser ) ) {
groupChatUser.receiveMessage( groupName, chatUser.getUserId(), message );
}
}
}
}
}
package com.javaspeak.designpatterns.go4.behavioural.mediator;
/**
*
* A ChatUser communicates with other ChatUsers using the mediator, ChatMediator. Only the
* ChatMediator has reference to all ChatUsers.
* <p>
* The mediator design pattern is about the Mediator only having reference to all consumers and
* producers. In this example the ChatUsers are both consumers and producers: they can both send
* and receive messages.
*
* author John Dickerson - 21 Feb 2020
*/
public interface ChatUser {
/**
* The userId of the ChatUser is the user name of the ChatUser
*
* @return userId
*/
public String getUserId();
/**
* The ChatUser calls this method to send messages to other ChatUser
*
* @param group
* @param message
*/
public void sendMessage( String group, String message );
/**
* The ChatMediator calls this message to send a message to the ChatUser
* which it has received from another user.
*
* @param group
* Group that the message belongs to
*
* @param userId
* The userId of the ChatUser sending the message
*
* @param message
* The message that is being sent to the ChatUser who has subscribed to the Group.
*/
public void receiveMessage( String group, String userId, String message );
/**
* A ChatUser calls this method to create a new Group.
*
* @param groupName
* The name of the group to create
*
* @return
* true if the group was created or false if the group already exists
*/
public boolean createNewGroup( String groupName );
/**
* Subscribe to the group
*
* @param groupName
* the group to subscribe to
*/
public void subscribeToGroup( String groupName );
/**
* Gets a list of the group Names that exist
*
* @return
* a list of the group names that exist
*/
public String[] getGroups();
/**
* Checks whether the GroupName is in the array of groups
*
* @param groupNames
* groups array of group names
*
* @param groupName the group name to check whether it is the array of
* group name or not
*
* @return true if the groupName is in the array of group names or false
* if it is not
*/
public boolean groupExists( String[] groupNames, String groupName );
}
package com.javaspeak.designpatterns.go4.behavioural.mediator;
/**
* A ChatUser communicates with other ChatUsers using the mediator, ChatMediator. Only the
* ChatMediator has reference to all ChatUsers.
* <p>
* The mediator design pattern is about the Mediator only having reference to all consumers and
* producers. In this example the ChatUsers are both consumers and producers: they can both
* send and receive messages.
*
* @author John Dickerson - 22 Feb 2020
*/
public class ChatUserImpl implements ChatUser, Comparable<ChatUserImpl> {
private String userId;
private ChatMediator chatMediator;
/**
* The ChatMediator is passed to each ChatUser implementation. The ChatUser makes calls to the
* ChatMediator to send messages to other ChatUsers
*
* @param chatMediator
* The ChatMediator holds references to other ChatUsers
*
* @param userId
* The userId of this ChatUser
*/
public ChatUserImpl( ChatMediator chatMediator, String userId ) {
this.chatMediator = chatMediator;
this.userId = userId;
}
@Override
public String getUserId() {
return userId;
}
@Override
public void sendMessage( String groupName, String message ) {
chatMediator.sendMessage( groupName, this, message );
}
@Override
public void receiveMessage( String groupName, String userId, String message ) {
System.out.println( this.userId + "> Received Message from : " +
userId + " [group: " + groupName + "], message :\n " +
message );
}
@Override
public boolean createNewGroup( String group ) {
return chatMediator.createNewGroup( group );
}
@Override
public void subscribeToGroup( String groupName ) {
chatMediator.subscribeToGroup( groupName, this );
}
@Override
public String[] getGroups() {
return chatMediator.getGroups();
}
@Override
public boolean groupExists( String[] groups, String groupName ) {
for ( String group : groups ) {
if ( groupName.equals( group ) ) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ( ( chatMediator == null ) ? 0 : chatMediator.hashCode() );
result = prime * result + ( ( userId == null ) ? 0 : userId.hashCode() );
return result;
}
@Override
public boolean equals( Object obj ) {
if ( this == obj )
return true;
if ( obj == null )
return false;
if ( getClass() != obj.getClass() )
return false;
ChatUserImpl other = ( ChatUserImpl )obj;
if ( chatMediator == null ) {
if ( other.chatMediator != null )
return false;
}
else if ( !chatMediator.equals( other.chatMediator ) )
return false;
if ( userId == null ) {
if ( other.userId != null )
return false;
}
else if ( !userId.equals( other.userId ) )
return false;
return true;
}
@Override
public int compareTo( ChatUserImpl other ) {
return this.userId.compareTo( other.userId );
}
}
Back: Gang of Four
Page Author: JD