Programmation évenementielle : mon Broadcaster pour ActionScript 2
Par -Alexandre LEGOUT aka LAlex- le mercredi, septembre 17 2003, 16:36 - AS2 - Lien permanent
La nouvelle méthode de programmation d'ActionScript 2 semble poser beaucoup de problèmes aux utilisateurs de la programmation évenementielle (dont je fais partie). En fait, mon problème essentiel avec l'AsBroadcaster existant porte sur deux points :
- On ne peut plus initialiser le prototype, mais uniquement l'instance (dans le constructeur)
- Si on veut une classe non-dynamique, il faut déclarer les fonctions addListener, removeListener et broadcastMessage.
Ma petite solution perso ne règle pas tous ces problèmes, dans le sens ou on ne peut pas faire de l'héritage dynamique, mais j'ai crée une classe nommée EventBroadcaster (je sais, le nom est pas top, mais j'ai pas osé LAlexBroadcaster, ca fait un peu mégalo ... :)).
class EventBroadcaster {
Elle peut être utilisée de deux manières :
var _listeners:Array;
// -- Constructeur
// Initialise le tableau '_listeners'
private function EventBroadcaster() {
this._listeners = new Array();
}
// Emet un évenement passé en paramètre
public function broadcastMessage (msg:String) {
// Parcoure le tableau '_listeners'
for (var i=0 ; i<this._listeners.length ; i++) {
// Si l'évenemet est présent dans un écouteur, on l'applique
// Avec les arguments passés aprés le nom de l'évenement
if (typeof this._listeners[i][msg] == "function") {
this._listeners[i][msg].apply(this._listeners[i],arguments.slice(1));
}
}
}
// Rajoute un écouteur a un objet
public function addListener(o:Object) {
// Supprime d'abord l'écouteur
this.removeListener(o);
// Ajoute l'objet en tant qu'écouteur
this._listeners.push(o);
}
// Supprime un écouteur
public function removeListener(o:Object) {
// Parcoure le tableau '_listeners'
// Si l'objet est trouvé, on le supprime et on arrete la boucle
for (var i=0 ; i<this._listeners.length ; i++) {
if (this._listeners[i] == o) {
this._listeners.splice(i,1);
break;
}
}
}
// Initialisation d'une instance de classe DYNAMIQUE
public static function initialize(o:Object) {
// On crée tout simplement le tableau '_listeners'
// et les méthodes pour broadcaster
o._listeners = new Array();
o.addListener = EventBroadcaster.prototype.addListener;
o.removeListener = EventBroadcaster.prototype.removeListener;
o.broadcastMessage = EventBroadcaster.prototype.broadcastMessage;
}
// Initialisation d'une classse DYNAMIQUE
// ou initialisation de la classe d'un instance DYNAMIQUE
// La classe doit être dinamique, tout simplement pour
// pouvoir faire appel a this.addListener ou this.broadcastMessage
public static function initializeClass(o:Object) {
var curParent:Object;
if (typeof o == "function") {
curParent = o.prototype;
} else {
curParent = o.__proto__;
}
do {
switch (curParent.__proto__) {
// La classe hérite deja de EventBroadcaster
case EventBroadcaster.prototype :
return;
// On est au niveau le plus bas de la chaine de protos
// On glisse le prototype dans la chaine
case Object.prototype :
curParent.__proto__ = EventBroadcaster.prototype;
return;
// Aucun des cas précédents, on remonte d'un cran dans
// la chaîne de prototypes
default :
curParent = curParent.__proto__;
}
} while (true);
}
}
- Par héritage
C'est de loin la méthode que je préfère, car elle ne nécessite pas une classe dynamique, ni une déclaration des méthodes de broadcast. Le seul inconvénient est que on n'a pas toujours le choix car l'héritage multiple n'existe pas en AS2. Mais on peut toujours faire hériter la classe mère de EventBroadcaster ...
class CustomClass extends EventBroadcaster {Et on l'utilise ainsi :
var _prop:Number = 10;
function CustomClass(prop:Number) {
this.addListener(this);
this._prop = prop;
}
function sendEvent() {
this.broadcastMessage("onEvent","Test d'évenement");
}
function onEvent(str) {
trace(str + " : " + this._prop);
}
}var myClass = new CustomClass(20);
myClass.sendEvent(); - Par l'utilisation de initialize ou initializeClass
- La méthode statique initialize fonctionne comme pour AsBroadcaster. Bien évidemment, il faut que la classe soit dynamique, pour pouvoir lui ajouter les fonction, et pouvoir appeler la méthode broadcastMessage dans les méthodes de la classe.
- La méthode initializeClass prend en paramètre soit une classe et fait hériter cette classe de EventBroadcaster, soit un objet et fait hériter la classe de cet objet de EventBroadcaster : c'est de l'héritage dynamique à l'aide de la chaine de prototypes.
dynamic class CustomClass {
var _prop:Number = 10;
function CustomClass(prop:Number) {
EventBroadcaster.initializeClass(this);
this._listeners = [];
this.addListener(this);
this._prop = prop;
}
function sendEvent() {
this.broadcastMessage("onEvent","Test d'évenement");
}
function onEvent(str) {
trace(str + " : " + this._prop);
}
}
Et l'utilisation est la même que celle donnée précédemment. L'avantage est que les méthodes addListener, removeListener et broadcastMessage ne se retrouvent pas dans chaque instance ...
Voila pour mon étude personnelle sur la question ! ![]()
Commentaires
Que te dire ... MERCI !!!!
Sans déconner, mais tu dors quand ?!? :O)
D'ailleurs, il est a noter que j'ai fais une petite erreur dans le premier code que j'ai donnée (rectifié maintenant).
En effet, dans le cas de l'héritage, j'avais mis dans le constructeur de la classe CustomClass les deux lignes
EventBroadcaster.initializeClass(this);this._listeners = [];
Ces lignes ne sont pas nécessaires, car un constructeur execute implicitement l'instruction super() (appel du constructeur de la classe mère), et donc pas besoin d'initialize, et encore moins de créer le tableau _listeners, car le constructeur de EventBroadcaster s'en occupe ...
Tiens ... pas bête pour le super(), c'est vrai que cela fonctionne en gros comme la méthode extends de Penner (version non documentée).
bye
Bon Boulot Lalex. Si je ne trompe pas, ta combinaison broadcastMesasge/removeListener contient le meme type de bug que ASBroadcaster en MX. Si tu enleves un listener pendant un broadcast tu risques de te retrouver avec un object qui ne recevera pas l'evenement. Il y a plein de details sur ce probleme dans ce thread sur Flashcoders:
http://chattyfig.figleaf.com/ezmlm/ezmlm-cgi?1:sss:54706#b
Et la solution en as de bokel/Fumio Nonaka (copie de l'array des listeners pour le broadcast):
http://chattyfig.figleaf.com/flashcoders-wiki/index.php?ASBroadcaster
Plus, bien sur, la version optimize de Bokel/Shinya Tomikawa pour MX:
http://www.helpqlodhelp.com/scripts/com.qlod.ExtendedBroadcaster.as
Plein de references pour creer le parfait broadcaster pour MX 2004
En plus ,je ne sais pas si c'etait vraiment la peine de se precipiter. Il y a deja deux broadcasters dans les classes 2004 (en plus de ASBroascaster qui est toujours la):
mx.transitions.BroadcasterMX
mx.events.EventDispatcher
Cela dit, Je n'ai pas encore regarde le detail de ces broadcasters pour savoir s'ils ont les problemes dont tu parles. Apres un bref coup d'oeil, j'ai l'impression que BroadcasterMX contient aussi ce bug de broadcast mais dans l'autre sens cette fois (boucle en decrement).
Note en plus, apparemment, ASBroadcaster n'a plus le bug qu'il avait sous MX:
http://chattyfig.figleaf.com/ezmlm/ezmlm-cgi?1:mss:87825:200309:kanekkghgfgkibiglodn
En fait, je n'avais pas vraiment pris le temps de ragarder correctement la classe BroadcasterMX ... :roll: Elle semble bien, sauf que je lui aurai rajouté la partie initializeClass de la mienne. En fait, le mieux (je pense) est de faire hériter EventBroadcaster de BroadcasterMX, en lui rajoutant ma méthode ....
L'héritage fonctionne pour les deux classes BroadcasterMX et EventDispatcher, mais le premier me pose le problème de l'initialisation du prototype, et le deuxième me pose le problème du changement de syntaxe, qui n'amène rien de plus, sauf qu'il faut changer tous les appels d'instructions ... :?
> le premier me pose le problème de l'initialisation du prototype
Est-ce vraiment un probleme? En parcourant les classes, j'ai vu que la classe mx.transitions.Tween utilise broadcasterMX.initialize tel quel pour initialiser son propre prototype. Ca n'a pas l'air mechant.
Sinon je viens de regarder un peu plus longuement broadcasterMX et j'ai dit une connerie :o. il n'y a pas de bug puisque la methode broadcastMessage fait bien une copie de l'array des listeners. Donc pas de soucis de ce cote la (mais je trouve que le code de Bokel reste quand meme le plus beau ;)).
Comme toi, je ne comprend pas pourquoi MM distribue autant de broadcasters avec des syntaxes differentes. Ca devient difficile d'avoir une idee generale et de savoir lequel utiliser. Cela dit, je te trouve dur avec EventDispatcher :). Il amene quand meme plus que seulement le changement de syntaxe puisqu'il permet maintenant a un object de s'enregister pour un seul evenement d'une source. Avec ASBroadcaster/BroadcasterMX, pour chaque evenement, tous les listeners sont invoques, meme s'ils ne se sont pas enregistres a cet evenement en particulier. Dans ton code pour EventBroadcaster, tu faisais mieux en testant l'existance de l'event handler d'abord mais ce n'etait pas la solution ideale.
Je pense que EventDispatcher a un beau futur devant lui mais malheureusement, comme d'habitude les premieres versions sont buggy. Il y a un thread a ce sujet sur Flashcoders, tu as du deja le lire mais je place le lien ici quand meme puisque c'est le sujet:
http://chattyfig.figleaf.com/ezmlm/ezmlm-cgi?1:sss:87833:enbmdpphjnoopfebobcb
Fil des commentaires de ce billet