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.