septembre
2009
Avec un titre pareil, toi, esprit perspicace, tu auras compris de qui je veux parler, n’est-ce pas ?
Et puisque que je ne t’apprendrai rien sur lui, je vais plutôt te parler de notre histoire à tous les deux, de notre rencontre, du temps qu’on a pensé ensemble, des infidélités, de notre séparation, de nos retrouvailles, etc…
Il existe de nombreux patrons de conceptions et s’il faut en choisir un pour commencer, lequel s’accorde parfaitement avec le chiffre 1 ?
Le singleton bien évidemment puisque son but est de restreindre l’instanciation d’une classe à un seul objet.
=> Rencontre du 1° type
La première fois que je l’ai aperçu, c’est lors de travaux pratiques. Le prof voulait nous initier aux patrons de conception mais plutôt que de partir dans une présentation théorique, il a simplement écrit quelques noms de ces « saints » patrons au tableau et demandé à ce qu’on en choisisse deux pour lesquels on lui ferait un petit compte rendu. Mon choix s’était porté sur Singleton et Adapter et on ne peut m’accuser d’avoir jouer petit dans la mesure où je ne les connaissais pas encore.
Pour ceux qui l’ignorent, une petite explication dynamique :
Puisqu’on ne veut pas créer plusieurs instances (objets) d’un même type (classe), la première idée consiste à interdire aux objets clients d’appeler le constructeur de notre type Singleton donc il faut le mettre en visibilité privée :
private Singleton(){};
C’est à ce moment là qu’un petit malin dit : « Ouais, mais comment on le crée alors ? »
On répond que comme les méthodes privées ne peuvent être appelées que de la classe elle même, l’instance unique sera créée dans la classe Singleton.
La question devient : « Ok, supposons qu’on a créée cette unique instance dans la classe Singleton, comment la récupère t’on dans la classe cliente ? »
De l’extérieur, comme on ne dispose pas encore d’objet, on ne peut faire appel qu’à une méthode statique publique de la classe Singleton :
public static Singleton getInstance()
En fusionnant les deux idées précédentes, on pourrait écrire
public static Singleton getInstance()
{
return new Singleton();
};
mais cela ne fait que rajouter un niveau d’indirection et on n’est pas plus avancé puisque qu’il suffit au client de faire plusieurs appels à getInstance() pour obtenir plusieurs instances de Singleton.
Puisqu’on veut obtenir une instance unique, on sent bien que l’appel à getInstance() doit nous retourner toujours le même objet et donc qu’il faut conserver sa référence dans une variable de classe :
private static Singleton instance;
La méthode devient :
public static Singleton getInstance()
{
if (instance == null)
instance = new Singleton();
return instance;
};
Si elle est indéfinie (au premier appel), on crée une instance que l’on mémorise et qu’on retournera les fois suivantes (les appels successifs)
Ce qui semble LA solution évidente !
=> Premier rendez vous : La classe !
Quelques années plus tard, dans la vie active, j’ai repensé à ma chère et unique instance …
belle comme une déesse, je l’ai invoquée …
et je l’ai trouvé entière à souhait.
=> Duplicité : Ange & Démon
Malheureusement, au fil d’une conversation sur plusieurs fils (thread), je me suis rendu compte qu’elle n’était pas si unique que cela, elle avait une soeur jumelle qui n’habitait pas à la même adresse ! Oh my god, comment c’était possible ???
En fait, le code précédent rencontre des problèmes dans le cas d’application multithread (les détails de cette sombre histoire au 2.2)
=> Infidélités, à mon tour !
Les patrons c’est bien, ça nous guide, mais arrive un moment où petit scarabée rêve de liberté et ne veut plus suivre aveuglément maître yoda. Il veut expérimenté par lui même …
Pour éviter de voir double et de rentrer dans des histoires de « Ce n’est pas le bon moment, synchronisons nous … » j’ai désappris « le code standard » pour écrire :
private static Singleton instance = new Singleton();
public static Singleton getInstance()
{
return instance;
};
Un jour, je suis même aller voir du côté des classes « utilitaires » à méthodes statiques qui avaient aussi leurs charmes.
Puis déplorant le fait qu’en statique, on ne peut pas modifier un comportement par redéfinition, je me suis laissé tenter par un singleton à la dérive (le super singleton ayant alors un constructeur protected).
Plus tard, en réfléchissant à la manière de modifier le comportement d’un Singleton à constructeur privé (utiliser la composition au lieu de l’héritage), et constatant que par analogie, il suffisait de faire pareil avec une classe à méthodes statiques, je me suis rendu compte que je m’étais fourvoyé dans cette histoire.
=> Je te quitte !
Le coup de grâce me fut donnée le jour où j’ai réalisé que mon instance unique d’application avait des instances uniques de plusieurs services. J’avais abusé des bonnes choses.
Je suis donc retourné à la source. L’intérêt du singleton, c’est que plusieurs classes clientes partagent le même objet qui est l’unique représentant d’un certain type.
Dans le cas de mon application, il n’y a pas de classe cliente puisque c’est mon point d’entrée. Le singleton ne se justifie pas.
Ensuite, pour mes services applicatifs (service de trace, de traduction, …), le singleton était une solution si toutes les autres composants les attaquaient directement. Du moment où ces services sont injectés dans mon application (objet unique) et que ces composants ont une référence sur l’application, ce pattern n’est plus nécessaire.
=> … mais je ne t’ai pas oublié ;o)
Je l’ai utilisé à tord et à travers mais je ne le regrette pas, j’ai vu son intérêt et ses limites.
Il ne faut pas jeter le bébé avec l’eau du bain car il peut être utile.
Par exemple, le singleton peut implémenter des interfaces, alors qu’une classe à méthodes statiques ne le peut pas (ce qui n’est guère étonnant pour du code pas vraiment objet).
Mais n’oubliez pas que sa simplicité peut vous jouer des tours :
- Simplicité d’utilisation, il peut devenir l’anti-pattern du marteau en or.
- Simplicité d’apparence, si on reste sur « le code standard », il peut causer des bugs en environnement multithread.
C’est un pattern qu’il est déconseillé d’utiliser pour différentes raisons :
* Difficile à tester (impossible de mocker ou d’injecter une instance de test du singleton).
* A tendance à introduire du couplage dans le code.
* L’écriture d’un singleton Thread safe n’est pas facile.
* Difficile à surcharger du fait de son initialisation.
* L’instance du singleton (en Java) n’est en fait pas vraiment unique (il n’est unique que pour un ClassLoader donné, pas pour une VM).
* Un singleton aujourd’hui deviendra un « multiton » demain.
Source : http://www.stateofmind.fr/confluence/display/java/Singleton
Une manière (plus ou moins propre) de refactorer un singleton pour éviter à la classe d’avoir d’autres responsabilités que celle de créer un objet unique et pour le rendre testable.
http://www.stateofmind.fr/confluence/display/java/Eviter+les+singletons
En scala par exemple, si tu declares une class : ca correspond a une classe java, mais si tu declares un object, c’est du coup un singleton. Ca unifie le concept de statique et de singleton. De plus, ca evite les problemes de double check, statique vs singleton et autres joyeusetés.
On peut faire des choses tres clean (comme gerer de l’héritage, ou créer un singleton object companion d’une classe classqiue qui elle n’est pas un singleton)
tres pratique : par exemple, si on veut un objet de type emptyList instancié une seule fois pour gerer ls retours avec des listes vides
Apres ca n’empeche pas une surutilisation inutile (courante en java, a cause du coup important d’instanciation en java 1.3 et de l’héritage de struts), et de maniere générale, il est toujours possible de faire n’importe quoi
un petit exemple java/scala sur le singleton que j’aime bien : http://pbadenski.blogspot.com/2009/06/design-patterns-in-scala-singleton.html
on voit bien que le langage joue quand meme une bonne part sur la complexité.
@lunatix, je ne cherchai pas à convaincre, je relatai juste mon expérience avec les biais que cela peut induire. Il est toujours intéressant d’avoir d’autres points de vues ;o)
Que je te comprenne bien, d’après toi, les inconvénients d’une sur-utilisation de ce pattern seraient dû à une mauvais intégration dans nos langages. Si les langages permettaient de mieux définir des instances uniques de certains type, il n’y aurait plus de mauvaise utilisation ?
Et en scala par exemple, ça se fait comment ?
moué, pas convaincu. je crois aussi que le mauvais support des langages pour le singleton est a mettre en cause. En scala par exemple, il est facile de definir un objet comme etant unique dans la JVM/.net (le langage compile pour les deux mondes). C’est propre, et tres utile.
du coup, on peut utiliser le singleton pour beaucoup plus de choses. (si on veut un objet listeVide par exemple, etc..)
Alors, singleton, mauvaise pratique, ou juste mal intégré ?
@Blaise1
Le problème que tu soulèves Blaise est récurrent. En java, pour le développement d’application, il n’y a pas vraiment de socle applicatif reconnu ? Du coup, ça nous laisse assez libre d’implémenter notre propre architecture avec tous les avantages et les risques que ça procure.
Le développement d’application est un socle fertile aux sur-utilisations du singleton dans la mesure où dans une application, en plus d’elle, il y a plein d’autre objet unique : gestionnaire de données, système d’authentification, système de trace, système de traduction, etc …
Objets qui pour la plupart peuvent avoir plusieurs implémentations (et selon moi, le singleton n’est pas adapté à l’héritage) Dans ce cas, je trouve que l’inversion de contrôle et l’injection de dépendance sont beaucoup plus adaptés. Au lancement, on charge un « graphe applicatif » (l’application et tous les services qui gravitent autour; en s’enregistrant auprès de l’application, il suffit que les composants utilisent ces services)
@Philippe
Bienvenu à l’association des SingletonAnonyme !!!
Pauvre petit singleton…
Abuse depuis son plus jeune age, et mis au pilori deux fois en une semaine…
allez, un bloggeur C++ pour le coup de grâce ?
Tu devrais insister un peu plus sur l’effet anti-pattern du Singleton.
Nous avons une application (Swing) très ancienne qui à été écrite tout au début par des gens qui débutaient et qui tout comme toi trouvaient que le singleton était LA solution…
Aujourd’hui on ne peux même pas écrire et tester un écran (JFrame, JPanel et compagnie) sans devoir démarrer l’application car tous nos composants de bases usent et abusent de ce pattern que je hais de plus en plus (accès aux dictionnaires de traduction, aux connexion de base de données).
Hormis cet aspect « lourd » il y aussi le fait que tout est singleton, les daos, les connexions, les effets visuels et même certains modèles. Au final nous n’avons aucuns design pattern autre que celui la, pas de Factory pour les daos ni pour les connexions j’en passe et des (bien) plus pratiques.
Bref, je dirais que le Singleton c’est à consommer avec modération et réflexion.