Communication entre objets
L’événementiel : être à l’écoute
À l’image des systèmes d’exploitation événementiels, vos objets seront sûrement amenés à être à l’écoute d’autres objets. Par exemple, votre formulaire graphique sera « attentif » aux actions de la souris pour pouvoir réagir immédiatement aux demandes de l’utilisateur. Votre formulaire ne sait pas quand l’utilisateur va cliquer sur tel ou tel composant, et ce sera d’ailleurs le composant lui-même qui l’appellera pour lui signaler son changement d’état.
Un objet captant des informations peut les diffuser à des « clients » qui se seront préalablement abonnés à sa liste de diffusion.
La programmation orientée objet est toujours très proche de la réalité... Vous êtes abonné à votre revue préférée comme des milliers d’autres lecteurs. Les journalistes écrivent des articles qui sont regroupés dans des éditions du journal. Périodiquement, ce journal vous est transmis. Vous pouvez à tout moment arrêter votre abonnement et en souscrire un autre ailleurs.
En programmation, c’est la même chose ; ce sont des mécanismes de gestion de liste d’abonnés...
Le pattern Observateur
1. Généralités
Cette problématique étant récurrente, le « gang des quatre » a conçu un patron de conception (design pattern) pour la résoudre.
Ce pattern, appelé Observateur, propose une solution de codage à une relation d’un (observé) à plusieurs (observateurs) en utilisant le couplage (interdépendance) le plus faible possible. Cette solution, qui a fait ses preuves, peut être utilisée directement en langage Java grâce à la classe java.util.Observable et à l’interface java.util.Observer.
Depuis le JDK 9, la classe java.util.Observable a été dépréciée, ce qui signifie que l’on peut continuer à l’utiliser avec ce JDK, mais qu’il faut s’attendre à la voir disparaître dans les versions à venir. Il existe évidemment des solutions de remplacement, mais la simplicité d’utilisation de la classe java.util.Observable fait qu’elle reste l’outil d’apprentissage idéal pour faire communiquer des objets Java entre eux.
Voici la représentation UML de ce type de liaison. Les noms des classes, des interfaces et des membres ne correspondent pas à ceux de l’API Java car c’est une représentation générique du design pattern.
La classe Observable et l’interface IObservateur ont été conçues en même temps, lors du développement d’un objet devant communiquer ses changements d’état à ses abonnés.
Dans un second temps, vous développez un programme devant, entre autres, traiter les notifications de cet objet. Cet objet est commercialisé depuis longtemps et fonctionne parfaitement.
La communication entre ces deux objets - pourtant développés à des moments différents - pourra être possible grâce au duo gagnant interface et polymorphisme. En effet la classe Observable ne connaît pas votre objet mais ; en revanche, votre objet peut implémenter l’interface IObservateur qui a été fournie par le développeur de l’objet Observable.
Quand votre objet va s’enregistrer sur l’Observable, il ne va pas se déclarer avec son type natif, mais...
Exercices
1. Exercice 1
a. Énoncé
Cet exercice consiste à écrire un programme qui crée plusieurs fenêtres (de type JFrame) repérées par un numéro dans leurs barres de titres et qui trace les événements « appui » et « relâchement » de la souris dans chacune de ces fenêtres. On signalera chaque événement en affichant sur la sortie console d’IntelliJ IDEA un message précisant sa nature (appui ou relâchement), le numéro de fenêtre et les coordonnées du pointeur souris au moment de l’événement.
Pour cela, nous implémenterons un listener plus spécialisé que actionListener : nous utiliserons mouseListener.
b. Corrigé
Créez un nouveau projet dans IntelliJ IDEA et appelez-le lab_mouse_ listener.
Ajoutez une classe appelée Fenetre au projet.
Faites étendre la classe Fenetre de la classe JFrame (profitez de l’assistant IntelliJ IDEA qui va ajouter la directive d’importation du package si vous activez [Alt][Entrée] sur le mot JFrame).
Ajoutez à la classe Fenetre l’implémentation de l’interface MouseListener en profitant également de l’assistant IntelliJ IDEA qui va ajouter d’un seul coup les cinq méthodes obligatoires.
Effacez les contenus de ces cinq méthodes.
Ajoutez un constructeur à la classe Fenetre pour :
-
gérer la numérotation unique des fenêtres à l’aide d’une donnée membre de type entier static et d’une seconde donnée membre de type entier instancié ;
-
définir des dimensions de fenêtre par défaut à l’aide de la méthode setBounds ;
-
s’enregistrer en tant que MouseListener à l’aide de la méthode addMouseListener ;
-
définir le comportement de l’application sur clic de la croix dans la barre de titre.
Dans la méthode mousePressed, affichez dans la fenêtre de sortie d’IntelliJ IDEA le type d’action, le numéro de la fenêtre concernée et les coordonnées du pointeur. Les coordonnées sont accessibles depuis l’objet MouseEvent passé en paramètre....
Appels synchrones, appels asynchrones
Une notion importante concerne le mode d’exécution d’une méthode par rapport au programme qui l’utilise. En effet, certaines méthodes peuvent prendre un temps non négligeable pendant leurs exécutions. Cette notion de temps est tout à fait subjective et dépend du contexte d’utilisation. Par exemple, en bureautique, une exécution d’une demi-seconde n’est pas préjudiciable ; dans le domaine industriel, ce n’est absolument pas la même chose...
Le fonctionnement synchrone est le mode d’exécution par défaut. Le programme appelant reste bloqué pendant le travail de la méthode appelée.
Voici la représentation UML type diagramme de séquences d’un appel synchrone d’une instance Object1 vers une instance Object2.
Si la durée d’exécution devient critique, il faut exécuter la méthode de façon asynchrone. Dans ce cas, l’exécution se divise en deux, autorisant les deux branches d’instructions à évoluer dans un mode « pseudo parallèle ».
Voici la représentation UML-Diagramme de séquences d’un appel asynchrone d’une instance Object1 vers une instance Object2.
La programmation d’exécutions parallèles passe par une programmation...