Dernièrement, j’ai été amené à développer en Java un système de gestion de comptes utilisateurs pour l’université dans laquelle j’étudie. Une fois de plus, j’ai du faire face à la nécessité de gérer la communication entre les différents modules composant mon application. Et une fois de plus, j’ai perdu patience.

L’interface n’est pas très complexe, mais la modularité des composants graphiques apporte une difficulté concernant la transmission des données des uns aux autres. Autrement dit c’est un bordel monstre.
Jusqu’à présent je n’avais pas trouvé de solution esthétique et simple à implémenter qui permettait à tous mes modules (en l’occurence des JPanel) de correspondre les uns avec les autres. Parmi les solutions disponibles mais, à mon sens, peu efficaces, notons:

  • l’utilisation des classes privées. Principal défaut: vous vous retrouvez avec des fichiers sources illisibles. Au delà de deux classes déclarées dans un même fichier on commence à s’y perdre sérieusement. L’autre défaut est que vous ne pouvez pas réutiliser vos composants ailleurs…
  • l’utilisation du design pattern observateur. Parfait quand une classe doit correspondre avec une autre et que vous pouvez accéder aux deux au même endroit pour créer la liaison. Le problème dans mon cas était que A devait communiquer avec D, qui était déclaré dans C, lequel était lui même déclaré dans B. Au final ça revenait à peu près au même que d’utiliser…
  • la dernière solution, qui consiste à fait en sorte de pouvoir traverser toute la chaîne des composants, des parents aux enfants. Et ça c’est moche.

J’ai donc décidé de coder un petit système de communication reprenant le concept des signaux utilisés notamment en ActionScript.

Présentation du concept.
L’idée consiste à créer un objet accessible par tous au moyen d’une méthode statique, un singleton que nous appellerons Dispatcher, et qui serait chargé de centraliser la gestion de la communication entre les divers objets à travers l’utilisation de signaux. L’accès au dispatcher est libre: n’importe quel objet peut envoyer et écouter n’importe quel signal.

Un signal est en fait un simple String, et les objets désirant écouter des signaux doivent implémenter une interface, DispatcherListener, et s’enregistrer auprès du dispatcher.

Par la suite, le fonctionnement du système suit la logique suivante:

  1. Un objet demande au dispatcher d’envoyer, par exemple, le signal « S1″ couplé à un Object contenant une donnée quelconque.
  2. À l’intérieur du dispatcher, un objet SignalEvent encapsulant le nom du signal, l’Object associé et la source de l’émission est crée.
  3. Le dispatcher récupère la liste des écouteurs enregistrés pour ce signal.
  4. Chacun des écouteur est notifié grâce à l’appel de la méthode signalReceived(SignalEvent)

schémas

Application
Nous devons créer deux classes et une interface:

  • L’interface DispatcherListener qui nous permettra d’indiquer aux écouteurs la méthode qu’ils doivent implémenter pour que le dispatcher puisse les notifier.
  • La classe SignalEvent qui encapsule les données relatives au signal qui a été envoyé.
  • Enfin, notre singleton Dispatcher qui gérera l’acheminement des signaux ainsi que l’enregistrement et la suppression des écouteurs.
    J’ai utilisé la structure de données HashMap pour associer les signaux aux liste d’écouteurs: les clés sont les signaux et les données sont des ArrayList. À l’origine je voulais placer les écouteurs dans des WeakReference, mais j’ai dû abandonner l’idée quand j’ai réalisé que cette décision m’empêchait de créer les écouteurs à l’aide de classes anonymes (ce qui explique au passage pourquoi les développeurs de swing n’ont pas retenu cette option). Quoiqu’il en soit, les fonctions d’accès et de modification sont toutes synchronisées pour éviter les incohérences éventuelles dues à la manipulation du dispatcher par plusieurs threads en même temps.

Les sources sont disponibles ici.