Principe du Médiateur
Le pattern de conception Médiateur introduit un objet intermédiaire qui gère les interactions complexes entre un ensemble d'objets. Cette approche évite des références directes entre ces objets, favorisant un couplage faible. Ainsi, la modification de leur interaction ne nécessite pas de changer chaque objet endividuellement.
Bien que la décomposition d'un système en nombreux objets améliore la réutilisabilité, la multiplication de leurs connexions peut créer des dépendances enchevêtrées. Le système se comporte alors comme un monolithe. L'introduction d'un médiateur transforme cette structure en réseau en une structure en étoile centrée sur le médiateur.
Structure et implémentation
Le cœur du pattern repose sur deux abstractions principales :
- Un
Médiateur(ouHub de communication), qui définit l'interface pour communiquer avec les objets collègues. - Des
Collègues(ouParticipants), qui détiennent une référence vers le médiateur et l'utilisent pour échanger des messages.
Ci-dessous, une implémentation illustrative en Java utilisant des interfaces et une logique de routage différente :
// Interface pour le médiateur
interface CommunicationHub {
void relayMessage(String content, Participant sender);
}
// Classe abstraite pour les participants
abstract class Participant {
protected final CommunicationHub hub;
protected Participant(CommunicationHub hub) {
this.hub = hub;
}
public abstract void receiveNotification(String content);
public void dispatch(String content) {
hub.relayMessage(content, this);
}
}
// Implémentation concrète du hub
class CentralHub implements CommunicationHub {
private Participant nodeA;
private Participant nodeB;
public void registerNodes(Participant first, Participant second) {
this.nodeA = first;
this.nodeB = second;
}
@Override
public void relayMessage(String content, Participant sender) {
// Logique de routage : le message est envoyé à tous les autres participants
if (sender != nodeA && nodeA != null) {
nodeA.receiveNotification(content);
}
if (sender != nodeB && nodeB != null) {
nodeB.receiveNotification(content);
}
}
}
// Participants concrets
class NetworkNode extends Participant {
public NetworkNode(CommunicationHub hub) {
super(hub);
}
@Override
public void receiveNotification(String message) {
System.out.println("Nœud a reçu : " + message);
}
}
class ServiceClient extends Participant {
public ServiceClient(CommunicationHub hub) {
super(hub);
}
@Override
public void receiveNotification(String message) {
System.out.println("Client a reçu : " + message);
}
}
Exemple d'utilisation :
CentralHub centralHub = new CentralHub();
NetworkNode node = new NetworkNode(centralHub);
ServiceClient client = new ServiceClient(centralHub);
centralHub.registerNodes(node, client);
// Les communications passent par le hub
node.dispatch("Statut : opérationnel");
client.dispatch("Requête de données");
Analyse : forces et limites
Avantages :
- Découplage : Les classes
Participantne connaissent plus directemnet les autres. Leur modification ou réutilisation devient indépendante. - Centralisation des contrôles : La logique de coordination est extraite et encapsulée dans le médiateur. Cela offre une vue d'ensemble du comportement d'interaction du système.
Inconvénient principal :
- Le médiateur peut devenir un "Dieu Object" : Si la logique d'intreaction devient très complexe, la classe
CentralHubpeut croître excessivement, devenant difficile à maintenir.
Quand l'utiliser
Ce pattern est particulièrement pertinent dans deux scénarios :
- Lorsque de nombreux objets doivent communiquer de manière bien définie mais complexe, comme les composants d'une interface graphique (GUI) ou les nœuds d'un réseau.
- Lorsqu'il faut définir un comportement distribué entre plusieurs classes sans pour autant multiplier l'héritage, ce qui simplifie la structure de classes.