The Mediator Pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
Key Concepts
- Mediator: Interface for communicating with Colleague objects
- Concrete Mediator: Implements cooperative behavior by coordinating Colleague objects
- Colleague: Objects that communicate with each other through the Mediator
- Loose Coupling: Colleagues don't communicate directly with each other
Basic Implementation
Example 1: Basic Mediator Pattern
import java.util.*;
// Mediator interface
interface Mediator {
void sendMessage(String message, Colleague colleague);
void addColleague(Colleague colleague);
}
// Concrete mediator
class ConcreteMediator implements Mediator {
private List<Colleague> colleagues = new ArrayList<>();
@Override
public void addColleague(Colleague colleague) {
colleagues.add(colleague);
colleague.setMediator(this);
}
@Override
public void sendMessage(String message, Colleague sender) {
for (Colleague colleague : colleagues) {
// Don't send the message back to the sender
if (colleague != sender) {
colleague.receiveMessage(message);
}
}
}
// Advanced mediation - send to specific colleague type
public void sendToType(String message, Colleague sender, String type) {
for (Colleague colleague : colleagues) {
if (colleague != sender && colleague.getType().equals(type)) {
colleague.receiveMessage(message);
}
}
}
}
// Abstract colleague
abstract class Colleague {
protected Mediator mediator;
protected String name;
protected String type;
public Colleague(String name, String type) {
this.name = name;
this.type = type;
}
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public abstract void sendMessage(String message);
public abstract void receiveMessage(String message);
public String getName() { return name; }
public String getType() { return type; }
}
// Concrete colleagues
class ConcreteColleague extends Colleague {
public ConcreteColleague(String name, String type) {
super(name, type);
}
@Override
public void sendMessage(String message) {
System.out.println(name + " [" + type + "] sends: " + message);
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println(name + " [" + type + "] receives: " + message);
}
}
// Demo
public class BasicMediatorDemo {
public static void main(String[] args) {
Mediator mediator = new ConcreteMediator();
Colleague alice = new ConcreteColleague("Alice", "User");
Colleague bob = new ConcreteColleague("Bob", "User");
Colleague admin = new ConcreteColleague("Admin", "Administrator");
mediator.addColleague(alice);
mediator.addColleague(bob);
mediator.addColleague(admin);
System.out.println("=== Basic Communication ===");
alice.sendMessage("Hello everyone!");
System.out.println();
bob.sendMessage("Hi Alice!");
System.out.println();
admin.sendMessage("System maintenance at 2 AM");
}
}
Real-World Example: Chat Room System
Example 2: Chat Room Mediator
import java.util.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// Message class
class Message {
private String content;
private String sender;
private LocalDateTime timestamp;
private MessageType type;
public Message(String content, String sender, MessageType type) {
this.content = content;
this.sender = sender;
this.timestamp = LocalDateTime.now();
this.type = type;
}
// Getters
public String getContent() { return content; }
public String getSender() { return sender; }
public LocalDateTime getTimestamp() { return timestamp; }
public MessageType getType() { return type; }
@Override
public String toString() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
return "[" + timestamp.format(formatter) + "] " + sender + ": " + content;
}
}
enum MessageType {
TEXT, IMAGE, FILE, SYSTEM
}
// User roles
enum UserRole {
REGULAR, MODERATOR, ADMINISTRATOR
}
// Chat mediator interface
interface ChatMediator {
void sendMessage(Message message, User user);
void addUser(User user);
void removeUser(User user);
void createRoom(String roomName);
void joinRoom(User user, String roomName);
void leaveRoom(User user, String roomName);
}
// Concrete chat mediator
class ChatRoomMediator implements ChatMediator {
private Map<String, List<User>> rooms = new HashMap<>();
private List<User> allUsers = new ArrayList<>();
public ChatRoomMediator() {
// Create default room
rooms.put("general", new ArrayList<>());
}
@Override
public void addUser(User user) {
allUsers.add(user);
joinRoom(user, "general"); // Auto-join general room
System.out.println("User " + user.getName() + " joined the chat");
}
@Override
public void removeUser(User user) {
allUsers.remove(user);
// Remove user from all rooms
for (List<User> roomUsers : rooms.values()) {
roomUsers.remove(user);
}
System.out.println("User " + user.getName() + " left the chat");
}
@Override
public void createRoom(String roomName) {
if (!rooms.containsKey(roomName)) {
rooms.put(roomName, new ArrayList<>());
System.out.println("Room '" + roomName + "' created");
// Notify all users about new room
Message systemMessage = new Message(
"New room '" + roomName + "' has been created",
"System",
MessageType.SYSTEM
);
broadcastMessage(systemMessage, null);
}
}
@Override
public void joinRoom(User user, String roomName) {
if (rooms.containsKey(roomName)) {
rooms.get(roomName).add(user);
user.setCurrentRoom(roomName);
Message joinMessage = new Message(
user.getName() + " joined the room",
"System",
MessageType.SYSTEM
);
sendToRoom(joinMessage, user, roomName);
}
}
@Override
public void leaveRoom(User user, String roomName) {
if (rooms.containsKey(roomName)) {
rooms.get(roomName).remove(user);
Message leaveMessage = new Message(
user.getName() + " left the room",
"System",
MessageType.SYSTEM
);
sendToRoom(leaveMessage, user, roomName);
// Auto-join general room if leaving current room
if (roomName.equals(user.getCurrentRoom())) {
joinRoom(user, "general");
}
}
}
@Override
public void sendMessage(Message message, User sender) {
if (sender.getCurrentRoom() != null) {
sendToRoom(message, sender, sender.getCurrentRoom());
}
}
private void sendToRoom(Message message, User sender, String roomName) {
List<User> roomUsers = rooms.get(roomName);
if (roomUsers != null) {
for (User user : roomUsers) {
if (user != sender) { // Don't send to self
user.receiveMessage(message);
}
}
}
}
private void broadcastMessage(Message message, User sender) {
for (User user : allUsers) {
if (user != sender) {
user.receiveMessage(message);
}
}
}
// Admin methods
public void broadcastToAll(Message message) {
broadcastMessage(message, null);
}
public List<String> getRoomList() {
return new ArrayList<>(rooms.keySet());
}
public List<String> getUsersInRoom(String roomName) {
List<String> userNames = new ArrayList<>();
if (rooms.containsKey(roomName)) {
for (User user : rooms.get(roomName)) {
userNames.add(user.getName() + " (" + user.getRole() + ")");
}
}
return userNames;
}
}
// User class (Colleague)
abstract class User {
protected ChatMediator mediator;
protected String name;
protected UserRole role;
protected String currentRoom;
public User(String name, UserRole role) {
this.name = name;
this.role = role;
}
public void setMediator(ChatMediator mediator) {
this.mediator = mediator;
}
public void setCurrentRoom(String roomName) {
this.currentRoom = roomName;
}
public abstract void sendMessage(String content, MessageType type);
public abstract void receiveMessage(Message message);
// Getters
public String getName() { return name; }
public UserRole getRole() { return role; }
public String getCurrentRoom() { return currentRoom; }
}
// Concrete user implementations
class RegularUser extends User {
public RegularUser(String name) {
super(name, UserRole.REGULAR);
}
@Override
public void sendMessage(String content, MessageType type) {
Message message = new Message(content, name, type);
System.out.println("[" + currentRoom + "] " + message);
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(Message message) {
System.out.println("[" + currentRoom + "] " + name + " received: " + message);
}
public void joinRoom(String roomName) {
mediator.joinRoom(this, roomName);
}
public void leaveRoom(String roomName) {
mediator.leaveRoom(this, roomName);
}
}
class ModeratorUser extends RegularUser {
public ModeratorUser(String name) {
super(name);
}
public void createRoom(String roomName) {
((ChatRoomMediator) mediator).createRoom(roomName);
}
public void listUsersInRoom(String roomName) {
List<String> users = ((ChatRoomMediator) mediator).getUsersInRoom(roomName);
System.out.println("Users in room '" + roomName + "': " + users);
}
}
class AdministratorUser extends ModeratorUser {
public AdministratorUser(String name) {
super(name);
}
public void broadcastToAll(String message) {
Message systemMessage = new Message(message, "Admin Broadcast", MessageType.SYSTEM);
((ChatRoomMediator) mediator).broadcastToAll(systemMessage);
System.out.println("Admin broadcast: " + message);
}
}
// Demo
public class ChatRoomDemo {
public static void main(String[] args) {
ChatMediator chatMediator = new ChatRoomMediator();
// Create users
RegularUser alice = new RegularUser("Alice");
RegularUser bob = new RegularUser("Bob");
ModeratorUser charlie = new ModeratorUser("Charlie");
AdministratorUser diana = new AdministratorUser("Diana");
// Register users with mediator
chatMediator.addUser(alice);
chatMediator.addUser(bob);
chatMediator.addUser(charlie);
chatMediator.addUser(diana);
System.out.println("\n=== Chat Room Demo ===");
// Regular chat
alice.sendMessage("Hello everyone!", MessageType.TEXT);
bob.sendMessage("Hi Alice! How are you?", MessageType.TEXT);
// Create and join new room
charlie.createRoom("programming");
alice.joinRoom("programming");
bob.joinRoom("programming");
charlie.joinRoom("programming");
System.out.println();
alice.sendMessage("Anyone working on Java projects?", MessageType.TEXT);
bob.sendMessage("Yes, I'm learning design patterns!", MessageType.TEXT);
// Admin broadcast
System.out.println();
diana.broadcastToAll("Server maintenance in 30 minutes!");
// List users in room
System.out.println();
charlie.listUsersInRoom("programming");
// Leave room
System.out.println();
alice.leaveRoom("programming");
alice.sendMessage("Back in general chat!", MessageType.TEXT);
}
}
Advanced Example: Air Traffic Control System
Example 3: Air Traffic Control Mediator
import java.util.*;
// Aircraft types
enum AircraftType {
COMMERCIAL, CARGO, PRIVATE, MILITARY
}
// Aircraft status
enum AircraftStatus {
ON_STAND, TAXIING, WAITING, TAKEOFF, LANDING, IN_FLIGHT
}
// Position class
class Position {
private int x;
private int y;
private int altitude;
public Position(int x, int y, int altitude) {
this.x = x;
this.y = y;
this.altitude = altitude;
}
// Getters and setters
public int getX() { return x; }
public void setX(int x) { this.x = x; }
public int getY() { return y; }
public void setY(int y) { this.y = y; }
public int getAltitude() { return altitude; }
public void setAltitude(int altitude) { this.altitude = altitude; }
public double distanceTo(Position other) {
int dx = x - other.x;
int dy = y - other.y;
int dz = altitude - other.altitude;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
@Override
public String toString() {
return "(" + x + ", " + y + ")@" + altitude + "ft";
}
}
// Air Traffic Control Mediator
interface ATCMediator {
void registerAircraft(Aircraft aircraft);
void unregisterAircraft(Aircraft aircraft);
void requestTakeoff(Aircraft aircraft, String runway);
void requestLanding(Aircraft aircraft, String runway);
void reportPosition(Aircraft aircraft, Position position);
void sendWarning(String message, Aircraft recipient);
boolean isRunwayAvailable(String runway);
List<Aircraft> getNearbyAircraft(Aircraft aircraft, double radius);
}
// Concrete ATC Mediator
class ControlTower implements ATCMediator {
private List<Aircraft> aircraftList = new ArrayList<>();
private Set<String> occupiedRunways = new HashSet<>();
private Map<String, List<String>> communicationLog = new HashMap<>();
@Override
public void registerAircraft(Aircraft aircraft) {
aircraftList.add(aircraft);
communicationLog.put(aircraft.getCallSign(), new ArrayList<>());
logCommunication("Control Tower", aircraft.getCallSign(),
"Aircraft registered with control tower");
System.out.println("ATC: " + aircraft.getCallSign() + " registered");
}
@Override
public void unregisterAircraft(Aircraft aircraft) {
aircraftList.remove(aircraft);
communicationLog.remove(aircraft.getCallSign());
System.out.println("ATC: " + aircraft.getCallSign() + " unregistered");
}
@Override
public void requestTakeoff(Aircraft aircraft, String runway) {
if (isRunwayAvailable(runway)) {
occupiedRunways.add(runway);
aircraft.setStatus(AircraftStatus.TAKEOFF);
logCommunication(aircraft.getCallSign(), "Control Tower",
"Request takeoff on runway " + runway + " - APPROVED");
System.out.println("ATC: " + aircraft.getCallSign() + " cleared for takeoff on " + runway);
} else {
logCommunication(aircraft.getCallSign(), "Control Tower",
"Request takeoff on runway " + runway + " - DENIED");
System.out.println("ATC: " + aircraft.getCallSign() + " hold position, runway " + runway + " occupied");
}
}
@Override
public void requestLanding(Aircraft aircraft, String runway) {
if (isRunwayAvailable(runway)) {
occupiedRunways.add(runway);
aircraft.setStatus(AircraftStatus.LANDING);
logCommunication(aircraft.getCallSign(), "Control Tower",
"Request landing on runway " + runway + " - APPROVED");
System.out.println("ATC: " + aircraft.getCallSign() + " cleared to land on " + runway);
} else {
logCommunication(aircraft.getCallSign(), "Control Tower",
"Request landing on runway " + runway + " - HOLD");
System.out.println("ATC: " + aircraft.getCallSign() + " go around, runway " + runway + " occupied");
}
}
@Override
public void reportPosition(Aircraft aircraft, Position position) {
aircraft.setPosition(position);
logCommunication(aircraft.getCallSign(), "Control Tower",
"Position report: " + position);
// Check for potential conflicts
checkForConflicts(aircraft);
}
@Override
public void sendWarning(String message, Aircraft recipient) {
logCommunication("Control Tower", recipient.getCallSign(), "WARNING: " + message);
recipient.receiveWarning(message);
}
@Override
public boolean isRunwayAvailable(String runway) {
return !occupiedRunways.contains(runway);
}
@Override
public List<Aircraft> getNearbyAircraft(Aircraft aircraft, double radius) {
List<Aircraft> nearby = new ArrayList<>();
Position pos = aircraft.getPosition();
for (Aircraft other : aircraftList) {
if (other != aircraft && pos != null && other.getPosition() != null) {
double distance = pos.distanceTo(other.getPosition());
if (distance <= radius) {
nearby.add(other);
}
}
}
return nearby;
}
private void checkForConflicts(Aircraft aircraft) {
List<Aircraft> nearby = getNearbyAircraft(aircraft, 5.0); // 5 unit radius
if (!nearby.isEmpty()) {
String warning = "Traffic alert: " + nearby.size() + " aircraft nearby";
sendWarning(warning, aircraft);
for (Aircraft other : nearby) {
sendWarning("Maintain separation from " + other.getCallSign(), aircraft);
}
}
}
public void releaseRunway(String runway) {
occupiedRunways.remove(runway);
System.out.println("ATC: Runway " + runway + " now available");
}
public void printCommunicationLog(String callSign) {
System.out.println("\nCommunication log for " + callSign + ":");
List<String> logs = communicationLog.get(callSign);
if (logs != null) {
for (String log : logs) {
System.out.println(" " + log);
}
}
}
private void logCommunication(String from, String to, String message) {
String logEntry = from + " -> " + to + ": " + message;
communicationLog.get(to).add(logEntry);
}
}
// Aircraft class (Colleague)
abstract class Aircraft {
protected ATCMediator mediator;
protected String callSign;
protected AircraftType type;
protected AircraftStatus status;
protected Position position;
public Aircraft(String callSign, AircraftType type) {
this.callSign = callSign;
this.type = type;
this.status = AircraftStatus.ON_STAND;
}
public void setMediator(ATCMediator mediator) {
this.mediator = mediator;
mediator.registerAircraft(this);
}
public abstract void requestTakeoff(String runway);
public abstract void requestLanding(String runway);
public abstract void reportPosition(Position position);
public abstract void receiveWarning(String warning);
// Getters and setters
public String getCallSign() { return callSign; }
public AircraftType getType() { return type; }
public AircraftStatus getStatus() { return status; }
public void setStatus(AircraftStatus status) { this.status = status; }
public Position getPosition() { return position; }
public void setPosition(Position position) { this.position = position; }
}
// Concrete aircraft implementations
class CommercialAircraft extends Aircraft {
private String airline;
private int passengerCount;
public CommercialAircraft(String callSign, String airline, int passengerCount) {
super(callSign, AircraftType.COMMERCIAL);
this.airline = airline;
this.passengerCount = passengerCount;
}
@Override
public void requestTakeoff(String runway) {
System.out.println(callSign + " (" + airline + "): Requesting takeoff on runway " + runway);
mediator.requestTakeoff(this, runway);
}
@Override
public void requestLanding(String runway) {
System.out.println(callSign + " (" + airline + "): Requesting landing on runway " + runway);
mediator.requestLanding(this, runway);
}
@Override
public void reportPosition(Position position) {
System.out.println(callSign + ": Position report - " + position);
mediator.reportPosition(this, position);
}
@Override
public void receiveWarning(String warning) {
System.out.println("⚠️ " + callSign + " ALERT: " + warning);
}
public void boardPassengers() {
System.out.println(callSign + ": Boarding " + passengerCount + " passengers");
}
public void disembarkPassengers() {
System.out.println(callSign + ": Disembarking " + passengerCount + " passengers");
}
}
class CargoAircraft extends Aircraft {
private double cargoWeight;
private String cargoType;
public CargoAircraft(String callSign, double cargoWeight, String cargoType) {
super(callSign, AircraftType.CARGO);
this.cargoWeight = cargoWeight;
this.cargoType = cargoType;
}
@Override
public void requestTakeoff(String runway) {
System.out.println(callSign + " (Cargo): Requesting takeoff on runway " + runway);
mediator.requestTakeoff(this, runway);
}
@Override
public void requestLanding(String runway) {
System.out.println(callSign + " (Cargo): Requesting landing on runway " + runway);
mediator.requestLanding(this, runway);
}
@Override
public void reportPosition(Position position) {
System.out.println(callSign + ": Position report - " + position);
mediator.reportPosition(this, position);
}
@Override
public void receiveWarning(String warning) {
System.out.println("⚠️ " + callSign + " ALERT: " + warning);
}
public void loadCargo() {
System.out.println(callSign + ": Loading " + cargoWeight + " tons of " + cargoType);
}
public void unloadCargo() {
System.out.println(callSign + ": Unloading " + cargoWeight + " tons of " + cargoType);
}
}
// Demo
public class ATCDemo {
public static void main(String[] args) {
ATCMediator controlTower = new ControlTower();
// Create aircraft
CommercialAircraft flight123 = new CommercialAircraft("UAL123", "United Airlines", 180);
CommercialAircraft flight456 = new CommercialAircraft("DAL456", "Delta Airlines", 220);
CargoAircraft cargo789 = new CargoAircraft("FED789", 45.5, "Electronics");
// Register with control tower
flight123.setMediator(controlTower);
flight456.setMediator(controlTower);
cargo789.setMediator(controlTower);
System.out.println("=== Air Traffic Control Demo ===\n");
// Simulate airport operations
flight123.boardPassengers();
flight123.requestTakeoff("09L");
cargo789.loadCargo();
cargo789.requestTakeoff("09R");
// Report positions (simulate potential conflict)
System.out.println("\n=== In-flight Operations ===");
flight123.reportPosition(new Position(10, 15, 10000));
flight456.reportPosition(new Position(12, 18, 10050)); // Close to flight123
cargo789.reportPosition(new Position(50, 60, 8000));
// Landing sequence
System.out.println("\n=== Landing Operations ===");
flight456.requestLanding("09L");
flight123.requestLanding("09L"); // Should be denied initially
// Release runway and try again
((ControlTower) controlTower).releaseRunway("09L");
flight123.requestLanding("09L");
// Print communication logs
System.out.println("\n=== Communication Logs ===");
((ControlTower) controlTower).printCommunicationLog("UAL123");
((ControlTower) controlTower).printCommunicationLog("DAL456");
}
}
GUI Application Example
Example 4: UI Component Mediator
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
// Mediator interface for UI components
interface UIMediator {
void componentChanged(UIComponent component);
void registerComponent(UIComponent component);
}
// Abstract UI component
abstract class UIComponent {
protected UIMediator mediator;
protected String name;
protected boolean enabled = true;
protected boolean visible = true;
public UIComponent(String name) {
this.name = name;
}
public void setMediator(UIMediator mediator) {
this.mediator = mediator;
mediator.registerComponent(this);
}
public abstract JComponent getJComponent();
public void setEnabled(boolean enabled) {
this.enabled = enabled;
getJComponent().setEnabled(enabled);
}
public void setVisible(boolean visible) {
this.visible = visible;
getJComponent().setVisible(visible);
}
public String getName() { return name; }
public boolean isEnabled() { return enabled; }
public boolean isVisible() { return visible; }
protected void notifyMediator() {
if (mediator != null) {
mediator.componentChanged(this);
}
}
}
// Concrete UI components
class TextFieldComponent extends UIComponent {
private JTextField textField;
public TextFieldComponent(String name, int columns) {
super(name);
this.textField = new JTextField(columns);
this.textField.setName(name);
this.textField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
public void insertUpdate(javax.swing.event.DocumentEvent e) { notifyMediator(); }
public void removeUpdate(javax.swing.event.DocumentEvent e) { notifyMediator(); }
public void changedUpdate(javax.swing.event.DocumentEvent e) { notifyMediator(); }
});
}
@Override
public JComponent getJComponent() {
return textField;
}
public String getText() {
return textField.getText();
}
public void setText(String text) {
textField.setText(text);
}
}
class ButtonComponent extends UIComponent {
private JButton button;
public ButtonComponent(String name, String text) {
super(name);
this.button = new JButton(text);
this.button.setName(name);
this.button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
notifyMediator();
}
});
}
@Override
public JComponent getJComponent() {
return button;
}
public void setText(String text) {
button.setText(text);
}
}
class CheckBoxComponent extends UIComponent {
private JCheckBox checkBox;
public CheckBoxComponent(String name, String text) {
super(name);
this.checkBox = new JCheckBox(text);
this.checkBox.setName(name);
this.checkBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
notifyMediator();
}
});
}
@Override
public JComponent getJComponent() {
return checkBox;
}
public boolean isSelected() {
return checkBox.isSelected();
}
public void setSelected(boolean selected) {
checkBox.setSelected(selected);
}
}
class LabelComponent extends UIComponent {
private JLabel label;
public LabelComponent(String name, String text) {
super(name);
this.label = new JLabel(text);
this.label.setName(name);
}
@Override
public JComponent getJComponent() {
return label;
}
public void setText(String text) {
label.setText(text);
}
}
// Concrete mediator for login form
class LoginFormMediator implements UIMediator {
private Map<String, UIComponent> components = new HashMap<>();
private int loginAttempts = 0;
private static final int MAX_ATTEMPTS = 3;
@Override
public void registerComponent(UIComponent component) {
components.put(component.getName(), component);
}
@Override
public void componentChanged(UIComponent component) {
String componentName = component.getName();
switch (componentName) {
case "usernameField":
validateForm();
break;
case "passwordField":
validateForm();
break;
case "rememberMeCheckbox":
handleRememberMe();
break;
case "loginButton":
handleLogin();
break;
case "clearButton":
handleClear();
break;
}
}
private void validateForm() {
TextFieldComponent usernameField = (TextFieldComponent) components.get("usernameField");
TextFieldComponent passwordField = (TextFieldComponent) components.get("passwordField");
ButtonComponent loginButton = (ButtonComponent) components.get("loginButton");
boolean isValid = !usernameField.getText().trim().isEmpty() &&
!passwordField.getText().trim().isEmpty();
loginButton.setEnabled(isValid);
}
private void handleRememberMe() {
CheckBoxComponent rememberMe = (CheckBoxComponent) components.get("rememberMeCheckbox");
LabelComponent statusLabel = (LabelComponent) components.get("statusLabel");
if (rememberMe.isSelected()) {
statusLabel.setText("Login will be remembered");
} else {
statusLabel.setText("");
}
}
private void handleLogin() {
TextFieldComponent usernameField = (TextFieldComponent) components.get("usernameField");
TextFieldComponent passwordField = (TextFieldComponent) components.get("passwordField");
LabelComponent statusLabel = (LabelComponent) components.get("statusLabel");
String username = usernameField.getText();
String password = passwordField.getText();
loginAttempts++;
if ("admin".equals(username) && "password".equals(password)) {
statusLabel.setText("Login successful! Attempts: " + loginAttempts);
disableForm();
} else {
statusLabel.setText("Login failed! Attempts: " + loginAttempts + "/" + MAX_ATTEMPTS);
if (loginAttempts >= MAX_ATTEMPTS) {
statusLabel.setText("Too many failed attempts! Form disabled.");
disableForm();
}
}
}
private void handleClear() {
TextFieldComponent usernameField = (TextFieldComponent) components.get("usernameField");
TextFieldComponent passwordField = (TextFieldComponent) components.get("passwordField");
CheckBoxComponent rememberMe = (CheckBoxComponent) components.get("rememberMeCheckbox");
LabelComponent statusLabel = (LabelComponent) components.get("statusLabel");
usernameField.setText("");
passwordField.setText("");
rememberMe.setSelected(false);
statusLabel.setText("Form cleared");
validateForm();
}
private void disableForm() {
components.get("usernameField").setEnabled(false);
components.get("passwordField").setEnabled(false);
components.get("loginButton").setEnabled(false);
components.get("rememberMeCheckbox").setEnabled(false);
components.get("clearButton").setEnabled(false);
}
}
// Demo GUI Application
public class UIMediatorDemo extends JFrame {
private UIMediator mediator;
public UIMediatorDemo() {
mediator = new LoginFormMediator();
initializeUI();
}
private void initializeUI() {
setTitle("Mediator Pattern - Login Form");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.fill = GridBagConstraints.HORIZONTAL;
// Create components
LabelComponent usernameLabel = new LabelComponent("usernameLabel", "Username:");
TextFieldComponent usernameField = new TextFieldComponent("usernameField", 15);
LabelComponent passwordLabel = new LabelComponent("passwordLabel", "Password:");
TextFieldComponent passwordField = new TextFieldComponent("passwordField", 15);
passwordField.getJComponent().setToolTipText("Enter 'admin' and 'password'");
CheckBoxComponent rememberMeCheckbox = new CheckBoxComponent("rememberMeCheckbox", "Remember me");
ButtonComponent loginButton = new ButtonComponent("loginButton", "Login");
ButtonComponent clearButton = new ButtonComponent("clearButton", "Clear");
LabelComponent statusLabel = new LabelComponent("statusLabel", "Please enter credentials");
// Register components with mediator
usernameField.setMediator(mediator);
passwordField.setMediator(mediator);
rememberMeCheckbox.setMediator(mediator);
loginButton.setMediator(mediator);
clearButton.setMediator(mediator);
statusLabel.setMediator(mediator);
// Add components to frame
gbc.gridx = 0; gbc.gridy = 0;
add(usernameLabel.getJComponent(), gbc);
gbc.gridx = 1; gbc.gridy = 0;
add(usernameField.getJComponent(), gbc);
gbc.gridx = 0; gbc.gridy = 1;
add(passwordLabel.getJComponent(), gbc);
gbc.gridx = 1; gbc.gridy = 1;
add(passwordField.getJComponent(), gbc);
gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2;
add(rememberMeCheckbox.getJComponent(), gbc);
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 1;
add(loginButton.getJComponent(), gbc);
gbc.gridx = 1; gbc.gridy = 3;
add(clearButton.getJComponent(), gbc);
gbc.gridx = 0; gbc.gridy = 4; gbc.gridwidth = 2;
add(statusLabel.getJComponent(), gbc);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new UIMediatorDemo().setVisible(true);
}
});
}
}
Best Practices and Considerations
When to Use Mediator Pattern:
- Complex object interactions: When objects have complex communication logic
- Reusability: When you want to reuse individual components easily
- Decoupling: When you want to reduce dependencies between objects
- Centralized control: When you need centralized control over interactions
Benefits:
- Reduces coupling: Objects don't reference each other directly
- Simplifies object protocols: Replaces many-to-many with one-to-many
- Centralizes control: Interaction logic is in one place
- Improves reusability: Components can be reused more easily
Drawbacks:
- Mediator can become complex: Can become a "god object"
- Single point of failure: Mediator failure affects entire system
- Performance overhead: Additional layer of indirection
Implementation Tips:
- Define clear mediator interface with well-defined communication methods
- Use abstract colleague classes to provide common functionality
- Consider using events for more complex communication scenarios
- Keep mediator focused on coordination, not business logic
- Use dependency injection to provide mediator instances
The Mediator Pattern is particularly useful in GUI applications, chat systems, air traffic control, and any scenario where multiple objects need to communicate in a coordinated way without being tightly coupled.