98% du temps, je vous dirais que la création de Singletons dans votre application est une mauvaise pratique, à éviter (par expérience). Bon, il reste 2%, tout premièrement car le framework Flex utilise lui-même des Singletons (chaque application est un singleton, mais aussi PopUpManager, DragManager, CursorManager & co). Et parfois, ils peuvent vous sauver la mise pour éviter de mettre en branle des techniques de sioux pour arriver à ses fins.
C'est ce qui m'est arrivé hier pendant mes développements dans une situation assez spéciale que l'on ne rencontre pas tous les jours.
Le contexte.
Je développe actuellement une administration pour un SIG Web qui consiste en une série d'applications Flex. Ces applications sont indépendantes dans leur fonctionnement et sont chargées par une application-mère qui consiste simplement en un TabNavigator contenant des SWFLoader.
Un petit schéma pour expliquer la situation:
La contrainte
Ces applications communiquent toutes avec un seul et même serveur baptisé Aigle. Ce serveur ne peut, par utilisateur, ne recevoir qu'une seule requête à la fois et c'est ici la contrainte qui nous intéresse. Si le serveur reçoit 2 requêtes du même utilisateur, la 2 sera mise en attente pendant un certain temps. Si la sous-application 1 fait une requête assez longue et que l'on bascule sur un autre onglet pour faire quelque chose qui va envoyer un autre requête, on tombera donc sur ce problème.
Le système d'envoi de requêtes en 2 mots
Pour envoyer des requêtes au serveur, j'utilise un Singleton (oui, c'est le dernier) nommé "RequestManager". Celui-ci s'occupe d'envoyer les requêtes au serveur à la chaine. Quand on tente d'envoyer une requête, on va simplement alimenter une pile de requête qui se dépile lorsque l'on reçoit le retour d'une requête. On a donc une FIFO (si je ne m'abuse) qui me permet de ne pas me heurter à la contrainte imposée par le serveur.
La classe RequestManager se trouve dans une librairie, utilisée par tous les projets.
Première approche, chargement simple des SWF applicatifs avec SWFLoader
Dans un premier temps, j'ai tenté de simplement créer des new SWFLoader et de faire un load(pluginPath.swf). Dans ce cas-là, chaque SWF est dans une "sandbox" locale. Il n'y a aucun partage de classe, chaque SWF est une entité bien distincte. Si on charge 6 sous-applications, on aura donc 6 instances bien distinctes de RequestManager (ce sont des Singletons par rapport à chaque SWF).
Si l'utilisateur switch rapidement entre les applications, il pourra donc effectuer 2 requêtes simultanées.
Deuxième approche, chargement des SWF avec le loaderContext de l'application
C'est grâce à ce qui s'est dit au séminaire Farata Systems (comme quoi ça a été vite mis en application) que j'ai pu résoudre mon problème. Le problème de la première approche était que j'avais autant de RequestManager que d'applications chargées. Dans l'idéal, il me faudrait un seul RequestManager, un Singleton unique sur plusieurs applications.
La solution vient donc du loaderContext. Ce que j'appelai une "sandbox" un peu plus haut est en fait un contexte applicatif. Pour résoudre mon problème, il a suffit que je donne à chaque sous-application, le même contexte application que l'application mère:
// code application mère
_currentSWFLoaded = new SWFLoader;
_currentSWFLoaded.loaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
_currentSWFLoaded.addEventListener(ProgressEvent.PROGRESS, onLoadingProgress);
_currentSWFLoaded.addEventListener(Event.COMPLETE, onLoadingComplete);
_currentSWFLoaded.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEvent);
...
De cette manière, les définitions de classes sont partagées. Je me retrouve donc avec une seule instance de RequestManager (une seule instance static puisque l'on a plus qu'un seul RequestManager, pour toutes les sous-applications. Notez que "ApplicationDomain.currentDomain" référence le domaine applicatif associé à l'application mère.
De cette manière, toutes les requêtes entrent dans la même pile et je n'ai plus aucun problème de synchronisation
Conclusion
Les Singletons restent en général une mauvaise pratique. Même si tout semble plus simple avec un Singleton (encore plus quand les frameworks encouragent à les utiliser), réfléchissez bien avant d'en utiliser un. Cependant, dans ce cas, le Singleton a évité que je fasse des manipulations douteuses qui aurait cassé le fonctionnement classique de mon code. En effet, chaque application doit aussi fonctionner de manière autonome, je ne pouvais pas faire remonter les requêtes à l'application mère par exemple.
A vous de bien utiliser les Singleton à bon escient
Autres Tutoriaux Flex liés:
- Flex FileReference – Upload de fichiers avec la méthode upload() et browse()
- Flex FileReference – IOError lors de l'appel à download() [Résolu]
- Flex Debug – Obtenir la StackTrace (pile d'appels) n'importe où dans votre code
- Flash Player 10.1 et Adobe Air 2 enfin en Release !
- Master Class Advanced Flex @ Bruxelles, Retour en images










Question bête, pourquoi l'utilisation de Singleton est considérée comme une mauvaise pratique ?
J'avoue que moi même j'en utilise très (trop?) souvent car je n'ai pas d'autre idée qui me vienne à l'esprit
Pareil que Vincent, il m'arrive d'en utiliser, en particulier pour des notifications qui fonctionnent un peu comme les Alert, j'utilise Singleton + méthodes statiques
Merci pour les infos, c'est toujours bon de savoir quand on prend une mauvaise direction
Salut Vincent,
Pour faire simple, disons que le Singleton va à l'encontre de la programmation orientée objet. Créer un singleton revient basically à faire une variable globale. Le but de la programmation OO est d'avoir des objets les plus "atomiques" possible, c'est-à-dire des objets qui font ce qu'ils ont à faire et c'est tout. Le Singleton est généralement utilisé comme fourre-tout et le code peut devenir très "bordélique" rapidement.
Si tu regardes aussi, tu ne peux pas faire de l'héritage avec un singleton, encore une des grandes idées de l'OOP qui part en fumée. Et ceux qui font des tests unitaires de code te diraient qu'un Singleton est assez peu pratique à tester (difficile de déterminer son état).
Dans certains cas, ils sont indispensables pour garder une cohérence dans les applications mais quand on souhaite modulariser ses applications, on se rend compte que les Singleton brident cette étape et obligent à avoir un couplage fort entre classes.
Et là, c'est l'expérience qui parle ^^
N'hésites pas à poser tes questions, c'est comme cela qu'on apprend
Fabien
@switcherdav
Pour les "Alert", j'utilise une classe qui étend Alert avec quelques méthodes static, mais ce n'est pas vraiment comme un Singleton.
Fabien
Je vais un peu compléter ce qui a déjà été dit.
En effet, la plupart des gens se servent du Singleton en tant qu'accès global à des variables, car ils ne savent pas trop comment faire pour passer une dépendance à une classe ou plusieurs classes.
Le principe du pattern Singleton étant de s'assurer qu'il n'existe qu'une seule instance d'un objet dans l'application (et d'exposer un point d'accès global à cette instance), on voit bien que son utilisation est détourné.
Les variables globales sont l'ennemi de l'encapsulation, un des principes piliers de la POO. En gros, tout le monde peut faire n'importe quoi avec et n'importe où dans le code, on ne passe plus par une interface publique. (Paye ton debug)
Et lorsqu'on effectue des tests unitaires, c'est plus une galère qu'autre chose. D'une part, car il est impossible de substituer le Singleton dans la classe qui l'utilise. En effet la classe qui utilise le Singleton demande sa dépendance alors qu'il faudrait que cette dépendance lui soi passer (via une interface) pour faciliter la substitution (Tell, Don't ask). D'autre part, car entre chaque test, il est difficile de remettre le Singleton dans son état de départ. On peut imaginer créer une méthode reset() sur le Singleton, mais on sent bien qu'on est dans la bidouille et non plus dans le good design.
Il faut faire la différence entre singleton (une unique instance d'une classe dans une application) et Singleton (le design pattern a proprement parlé). D'ailleurs pour le request manager, je pense que là aussi, on peut éviter de passer par un Singleton. L'idée serait de l'initialiser dans l'application shell/socle/mère/parente/… et de passer cette instance aux modules quand ils sont chargés.
Merci pour ce complément d'information
Fabien
"Le principe du pattern Singleton étant de s'assurer qu'il n'existe qu'une seule instance d'un objet dans l'application (et d'exposer un point d'accès global à cette instance)"
Dans la cas de la classe Alert, est-ce que c'est ce qui se passe ?
Avec mon Singleton et ma classe static, c'est ce que je fais, n'importe qu'elle classe peut appeler mon alert par une méthode static show( ) pour autant, la classe ne sera instantié qu'une seule fois dans toute mon application
Perso, je n'utilise pas le Singleton pour stocker des variables globales mais pour servir de pont entre des composants MXML pour communiquer
un composant dispatch par l'intermédiaire du Singleton un évènement custom et il suffit aux autres composants d'être abonnés à cet évènement sur le Singleton pour être au courant
L'utilisation d'un évènement custom permet de passer des variables sans les stocker dans le Singleton ce qui serait effectivement une bien mauvaise idée
De cette manière les vues ne se connaissent pas entre elles et fonctionnent indépendamment
Jusqu'à présent je n'ai pas eu de soucis avec cette méthode, mais si ce qui est dit sur le Singleton est vrai, je devrais rencontrer des problèmes tôt ou tard
Mais bon, au moins je comprendrais concrètement pourquoi parce que pour l'instant, je reste un peu dans le brouillard
Merci à tous pour vos avis éclairés
Salut,
j'utilise aussi un Singleton pour le comportement que tu expliques, c'est à dire le dispatch d'évènements en "broadcast" que l'on peut intercepter un peu partout dans l'application. Le problème par exemple avec ce type d'approche est que si tu veux intercepter un évènement afin qu'il ne se propage pas aux autres composants (en ajoutant un EventListener avec un priorité plus haute, tu ne pourra pas.
Enfin c'est peut-être une problème d'implémentation de cette classe mais bon ^^.
Fabien
La classe Alert du Flex SDK n'est pas un singleton. Tu peux le vérifier en écrivant 2 Alert.show() à la suite (2 popups apparaissent)
Je pense que tu finira par avoir aussi des problèmes en utilisant un singleton en tant que central/global dispatcher.
Imagine un composant constitué de plusieurs éléments qui communiquent entre eux grâce à un global dispatcher sous la forme d'un singleton.
Si tu utilises ce composant 2 fois dans la même application, alors tu risques d'avoir des effets de bord car le 2eme composant va recevoir également les événements dispatchés par le premier.
Florian
Mais alors comment faire pour qu'une vue puisse en prévenir une autre (avec passage de variables) sans qu'il n'y ait de lien entre elles ?
Est-ce que le conteneur de mes vues doit servir d'intermédiaire : la vue A dispatch un event custom intercepté par le conteneur.
Le conteneur déclenche une méthode de la vue B avec passage de variables ?
C'est le Singleton en tant que classe statique qui est une mauvaise pratique ? On est d'accord donc.
Mais le singleton en tant qu'unique classe est-il recommandé finalement ? J'ai du mal à faire le tri dans tout ça.
C'est cette dernière définition que j'utilise souvent. Je prend l'exemple d'un manager qui va me charger un set de data quelconque. Personnellement ça me parait naturel d'utiliser le singleton toujours dans cette optique de découplage des objets.
Il me parait naturel que tous mes autres objets accèdent à la même source de donnée et ne multiplient pas les connexions vers ma source de données
"Mais le singleton en tant qu'unique objet d'une classe est-il recommandé finalement "
C'est comme ça qu'il faut le lire, j'ai du mal ce matin moi
Salut Vincent,
Pour faire simple, si tu veux faire avoir une seule et même instance pour toute ton application, le design pattern Singleton est la bonne voix. Par exemple pour la gestion de ton pool de connexion. Mais il est déconseillé de l'utiliser dans la majorité des cas (voir raisons plus haut). Parfois c'est indispensable, parfois non
Fabien