Mediator

La programmation avec design patterns

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
On connaît tous un médiateur. Dans de petites disputers dans une fraterie, c'était souvent le père. A l'école celui qui tranchait dans le domaine des conflits entre parents et le professeur, était souvent le directeur. Le médiateur existe également dans le monde de développement informatique, et plus précisément dans le monde des design patterns.

Le mediator (médiateur) est un objet qui synchronise le travail de plusieurs autres objets. La synchronisation se fait grâce à la communication entre les objets et le médiateur et non pas la communication directe entre les objets. Le médiateur peut donc influencer la façon de fonctionner d'un logiciel. Il s'agit donc d'un design pattern appartenant à la famille des patrons comportementaux.

Grâce à cette descritpion, on déduit la présence des 2 objets dans ce design pattern :
- mediator : celui qui orchestre la communication entre de différents autres objets
- colleagues (collègues) : ceux dont la communication est orchestrée par le médiateur

Les deux acteurs se basent sur les interfaces. Parfois on retrouve donc la présence des 4 acteurs : l'interface du médiateur et l'interface des collègues. Passons maintenant à l'exemple de ce design pattern.

Exemple du mediator
Pour illustrer l'utilisation du mediator, on peut créer un outil qui va synchroniser le chargement des éléments sur une page web. Afin de simplifier le concept, on va supposer qu'une page web est composée des 3 paires de fichiers. Chaque paire de fichiers contient une seule image, un seul paragraphe et un seul div. Le chargement de chaque élément d'un fichier du package provoque le chargement du même fichier dans d'autres packages. Regardons d'abord l'interface des colleagues :

abstract class PackageColleague {
protected PackageMediator mediator;
protected boolean divDownloaded = false;
protected boolean imgDownloaded = false;
protected boolean pDownloaded = false;

public PackageColleague(PackageMediator mediator) {
this.mediator = mediator;
}

public void downloadDiv() {
this.divDownloaded = true;
this.mediator.divDownloaded(this);
}

public void downloadImg() {
this.imgDownloaded = true;
this.mediator.imgDownloaded(this);
}

public void downloadP() {
this.pDownloaded = true;
this.mediator.pDownloaded(this);
}

public boolean isDivDownloaded() {
return this.divDownloaded;
}

public boolean isImgDownloaded() {
return this.imgDownloaded;
}

public boolean isPDownloaded() {
return this.pDownloaded;
}

}

Cette classe abstraite sera la classe de base pour les packages. On ne voit rien de particulier à part l'invocation du médiateur au chargement de chaque élément. Qu'est-ce que provoquent les méthodes divDownloaded(), imgDownloaed() et pDownloaded() ? Et comment se présente le médiateur ? Le voici :

class PackageMediator {

private List<PackageColleague> colleagues = new ArrayList<PackageColleague>();

public void addColleague(PackageColleague colleague) {
this.colleagues.add(colleague);
}

public void divDownloaded(PackageColleague colleagueWorker) {
for(PackageColleague colleague : colleagues) {
if (colleague.getClass() != colleagueWorker.getClass() && !colleague.isDivDownloaded()){
colleague.downloadDiv();
}
}
}

public void imgDownloaded(PackageColleague colleagueWorker) {
for(PackageColleague colleague : colleagues) {
if (colleague.getClass() != colleagueWorker.getClass() && !colleague.isImgDownloaded()){
colleague.downloadImg();
}
}
}

public void pDownloaded(PackageColleague colleagueWorker) {
for(PackageColleague colleague : colleagues) {
if (colleague.getClass() != colleagueWorker.getClass() && !colleague.isPDownloaded()){
colleague.downloadP();
}
}

}

}

Maintenant on y voit plus clair. Le chargement de chaque élément de la page provoque l'appel de la méthode du médiateur qui, elle, s'occupe de charger l'élément en question dans d'autres packages. Dans le médiateur on voit également une liste qui contient tous les collègues. D'ailleurs, ils se présentent de cette manière :

class FirstPackageColleague extends PackageColleague {

public FirstPackageColleague(PackageMediator mediator) {
super(mediator);
}

@Override
public void downloadDiv() {
System.out.println("Downloading div for #1 package");
super.downloadDiv();
}

@Override
public void downloadImg() {
System.out.println("Downloading img for #1 package");
super.downloadImg();

}

@Override
public void downloadP() {
System.out.println("Downloading p for #1 package");
super.downloadP();
}

}

class SecondPackageColleague extends PackageColleague{

public SecondPackageColleague(PackageMediator mediator) {
super(mediator);
}

@Override
public void downloadDiv() {
System.out.println("Downloading div for #2 package");
super.downloadDiv();
}

@Override
public void downloadImg() {
System.out.println("Downloading img for #2 package");
super.downloadImg();
}

@Override
public void downloadP() {
System.out.println("Downloading p for #2 package");
super.downloadP();
}
}

class ThirdPackageColleague extends PackageColleague{

public ThirdPackageColleague(PackageMediator mediator) {
super(mediator);
}

@Override
public void downloadDiv() {
System.out.println("Downloading div for #3 package");
super.downloadDiv();
}

@Override
public void downloadImg() {
System.out.println("Downloading img for #3 package");
super.downloadImg();
}

@Override
public void downloadP() {
System.out.println("Downloading p for #3 package");
super.downloadP();
}
}

On voit que les collègues ne font rien de spécial à part d'afficher sur l'écran l'élément actuellement chargé. Maintenant regardons comment se présente la méthode de test du médiateur :

PackageMediator mediator = new PackageMediator();

PackageColleague firstPackage = new FirstPackageColleague(mediator);
PackageColleague secondPackage = new SecondPackageColleague(mediator);
PackageColleague thirdPackage = new ThirdPackageColleague(mediator);

mediator.addColleague(firstPackage);
mediator.addColleague(secondPackage);
mediator.addColleague(thirdPackage);

firstPackage.downloadDiv();
secondPackage.downloadImg();
thirdPackage.downloadP();

On voit d'abord la création de l'objet PackageMediator. Ensuite son instance est passée au constructeur de chaque package. Après les packages sont rajoutés au médiateur. Tout à la fin on charge les fichiers. On commence avec le div du premier package pour enchaîner avec l'img du second et terminer avec le p du troisième. Si l'on analyse le résultat, on verra que le chargement d'un élément provoque le chargement du même élément dans d'autres packages :

Downloading div for #1 package
Downloading div for #2 package
Downloading div for #3 package
Downloading img for #2 package
Downloading img for #1 package
Downloading img for #3 package
Downloading p for #3 package
Downloading p for #1 package
Downloading p for #2 package


Le médiateur réduit donc les dépendances entre les collègues. Désormais ils ne communiquent pas entre eux directement, mais à travers du médiateur qui joue le rôle de l'encapsulateur. L'exemple le plus simple du médiateur dans le monde des applications web est le chat où le chatroom joue le rôle du médiateur et les participants sont des collègues.
Bartosz KONIECZNY 08-09-2013 17:38 design patterns
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil Symfony1

Comment inclure un template commun pour plusieurs modules ?

Le fichier à inclure (par exemple _menu.php) devrait être stocké dans le répertoire templates de l'application en question.

Ensuite, dans notre fichier de layout (par exemple layout.php), il suffit d'appeler le helper include_partial : Le répertoire global signifie que le template est global et n'appartient pas à un module particulier.