j’ai posté en décembre 2009 un article introductif sur les modèles de conception en java, où j’évoquais entre autre le modèle de conception de type observateur.
Aujourd’hui je vais expliquer comment mettre en oeuvre ce modèle avec ActionScript en créant des évènements personnalisés.

Un peu de théorie.
Le modèle événementiel repose, comme en java, sur le modèle de conception observateur. Il distingue:
- un sujet: l’objet écouté (par exemple, un bouton).
- un évènement: l’action de cliquer sur ce bouton.
- un écouteur: la fonction ou la méthode qui sera appelée lors de la diffusion de l’évènement.

Pour pouvoir écouter un évènement associé à sujet donné, il faut que ce dernier hérite de la classe EventDispatcher afin qu’il dispose des méthodes addEventListener et dispatchEvent qui permettent de définir un écouteur et de lancer un évènement.

Si on ne peut affecter plusieurs fois un même écouteur à un même couple (sujet, évènement), on peut en revanche affecter plusieurs écouteurs différents à un même couple.
Voici la signature permettant d’associer à un sujet un évènement et un écouteur:
DispatchEvent.addEventListener(type:String, ecouteur:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

Concrètement, cela revient à transmettre une référence de l’écouteur au sujet; c’est ainsi que le sujet pourra appeler la fonction désirée lorsque l’évènement associé aura été détecté.
Les deux paramètres les plus importantes transmis lors de l’appel à addEventListener sont:
- Le paramètre type est une chaîne de caractères identifiant le type de l’évènement que l’on cherche à diffuser.
- Le paramètre ecouteur désigne la fonction qui sera exécutée lorsque l’événement sera capté.

Un évènement diffusé est systématiquement accompagné d’un objet héritant de la classe Event. La fonction écouteur aura donc un paramètre du même type, appelé objet évènementiel, contenant entre autres les propriétés suivantes:
- currentTarget: le sujet de l’évènement diffusé.
- type: la chaîne de caractères identifiant le type de l’évènement.

Illustration pratique.
Voilà pour la théorie. Pour la pratique, je vais reprendre l’exemple que j’avais utilisé dans mon précédent article sur le modèle observateur.
Je le rappelle brièvement: on dispose de deux classes Cible et Observateur, et on veut notifier notre observateur lorsqu’une propriété de la cible est modifiée.

Commençons par la classe Cible. Cette classe doit offrir la possibilité de diffuser un évènement, elle doit donc hériter de EventDispatcher:

package {
	import flash.events.EventDispatcher;
	import flash.events.Event;
 
	public class Cible extends EventDispatcher {
 
		private var valeur:int;
 
		public function Cible(valeur:int) {
			super();
			this.valeur = valeur;
		}
 
		public function setValeur(valeur:int):void {
			valeur = this.valeur;
			dispatchEvent(new Event("evenement_perso"));
		}
 
		public function getValeur():int {
			return this.valeur;
		}
	}
}

Dans la mesure ou ActionScript ne gère pas l’héritage multiple, il est parfois nécessaire de trouver une alternative au fait d’étendre EventDispatcher. On préférera alors implémenter l’interface IEventDispatcher. Les appels aux fonctions de IEventDispatcher seront répercutés sur un objet EventDispatcher placé en propriété de Cible:

package {
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
 
	public class Cible implements IEventDispatcher {
 
		private var valeur:int;
 
		private var dispatcher:EventDispatcher;
 
		public function Cible(valeur:int) {
			this.valeur = valeur;
			this.dispatcher = new EventDispatcher();
		}
 
		public function setValeur(valeur:int):void {
			this.valeur = valeur;
		}
 
		public function getValeur():int {
			return this.valeur;
		}
 
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void {
			this.dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}
 
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void {
			this.dispatcher.removeEventListener(type, listener, useCapture);
		}
 
		public function dispatchEvent(event:Event):Boolean {
			return this.dispatcher.dispatchEvent(event);
		}
 
		public function hasEventListener(type:String):Boolean {
			return this.dispatcher.hasEventListener(type);
		}
 
		public function willTrigger(type:String):Boolean {
			return this.dispatcher.willTrigger(type);
		}
	}
}

Passons à présent au code de notre classe Observateur:

package {
	import flash.events.Event;
	import Cible;
 
	public class Observateur {
 
		private var cible1:Cible;
 
		private var cible2:Cible;
 
		public function Observateur() {
			cible1 = new Cible(5);
			cible2 = new Cible(10);
			cible1.addEventListener("evenement_perso", update);
			cible2.addEventListener("evenement_perso", update);
 
			cible1.setValeur(20);
		}
 
		public function update(event:Event):void {
			var cible:Cible = event.target as Cible;
			var valeur:int = cible.getValeur();
			trace("Nouvelle valeur récupérée");
		}
	}
}

Et voilà, la mise en oeuvre du modèle de conception observateur en ActionScript est aussi simple que cela. On pourra si nécessaire étendre la classe Event si l’on souhaite transmettre à l’écouteur des informations qui ne sont pas directement accessible à l’aide de la cible.

Maintenant, on pourrait se demander pourquoi passer par une structure aussi « sophistiquée » alors qu’on pourrait aboutir au même résultat en transférant simplement à la cible une copie de l’instance Observateur. De cette manière, la cible pourrait appeler directement la fonction update de l’observateur. En fait, l’utilisation des événements nous permet de conserver une certaine modularité dans notre application, et facilite ainsi la maintenance du code.