OVH Cloud OVH Cloud

[HS] Singleton, un anti-pattern ?

22 réponses
Avatar
Patrick Mézard
Bonjour,

Suite à la dernière itération de la discussion récurrente sur DCL et les
singletons, je me posais la question suivante : le "Singleton" est-il
vraiment utile à quelque chose ? A chaque fois que j'ai vu ce truc
apparaître comme par magie là où on ne l'attendait pas c'était pour une
des raisons suivantes :

1- Ca permet d'avoir des variables globales sous couvert de
respectabilité. Bah oui, c'est dans le légendaire _Design Patterns_, donc...

2- "C'est cool". C'est le seul motif de conception (encore merci
wikipedia, <http://fr.wikipedia.org/wiki/Motif_de_conception>) que j'ai
compris ou que j'arrive à utiliser alors je m'en sers. Sinon j'aurais lu
l'autre bouquin pour rien.

3- "C'est cool et ça en jette de complexité". Y a un chapitre entier sur
le single ultime dans _Modern C++ Design_. Tous les problèmes sont
réglés une fois pour toute : durée de vie, interdépendance,
multi-threading (ah tiens, du DCL !) et j'en passe. C'est tellement
subtil, ça ne peut pas être inutile ?

4- Ca fait beau dans une doc ou sur un schéma de conception.

5- "J'étais pourtant certain qu'il n'y en aurait qu'un !"

6- Ca permet de corriger d'autres problèmes de conception.

En définitive, existe-t-il des exemples justifiant l'emploi d'un
Singleton ? N'est-il pas toujours possible de créer une instance lors
d'une phase d'initialisation quelconque et de l'enregistrer auprès
d'éventuels autres objets ?

Comme toujours, la seule raison valide que je vois reste la [6] (une
mauvaise conception préalable justifie tout et n'importe quoi), auquel
cas le Singleton serait plus un motif de correction/réparation que de
conception.

Qu'en pensez-vous ?

Patrick Mézard

10 réponses

1 2 3
Avatar
Loïc Joly

2- Il arrive aussi d'utiliser std::cout de n'importe où,
particulièrement pour faire des "traces" (des enregistrements ?). Ce
genre d'utilisation me pose un vrai problème. En pratique, je ne veux
pas que l'existence des enregistreurs impacte le design de l'application
ou de la bibliothèque. Comme ce sont *a priori* des outils de
développement et pas des fonctionnalités, je ne veux pas passer ces
instances dans les fonctions, je dois accèder à des objets globaux. De
fait, je ne me souviens pas avoir vu de bibliothèque de logging sans
singleton/globale. Mais Loïc a peut-être un autre avis sur le sujet :-) ?


Disons que je n'ai rien contre l'utilisation de variables globales pour
ce genre de situation. Le fait d'utiliser un singleton me semblerait par
contre ajouter une contrainte supplémentaire et enlever des
fonctionnalités à l'utilisateur.

Par exemple, pour le logging, si l'objet de log est un singleton, il est
unique. Point. Si c'est juste une variable globale, même si le
concepteur du log ne l'a pas prévu, rien n'empêche l'utilisateur d'avoir
2 log, par exemple un mode succint et un mode détaillé, dans deux
fichiers différents avec deux objets différents.

J'ai l'impression que souvent les singletons représentent une limitation
artificielle et arbitraire. Dans beaucoup de cas, cette limitation n'est
pas gênante, mais quand elle le devient... Mettre du log en singleton,
c'est pour moi un peu comme si on décidait qu'un PC n'aurait jamais plus
de 640ko de mémoire.

--
Loïc

Avatar
kanze
Patrick Mézard wrote:

[...]

C'est donc un compromis entre l'isolation des composants et
leur simplicité d'utilisation. C'est drôle parce que j'ai
toujours fait l'effort de cloisonner les composants alors que
je n'ai travaillé que sur des projets de petite taille. Et
j'imaginais que ce besoin de cloisonnement se renforçait avec
la taille de la base de code. A chaque fois que j'ai reusiné
du code en enlevant des structures du type singleton j'ai eu
l'impression d'y gagner en clarté et de mettre en évidence
d'autres problèmes de conception plus profonds.


Le problème, c'est que souvent, il y a des spécifications dans
le cahier de charge qui ont un aspect universel. Donc, par
exemple, il est fréquent de rétrouver une exigeance que le log
soit configurable, de façon à ce qu'on peut envoyer les erreurs
les plus sévère par email, d'autres au syslog, et que la plupart
n'apparaissent que lors des mises à point, quand il vont dans un
fichier classique. On peut, effectivement, créer un objet de log
dans le main, qu'on passe par paramètre partout. Mais ça
introduit la possibilité que quelqu'un en créer deux, par
erreur ; dans ce cas-là, le singleton est un moyen d'assurer que
l'objet soit unique.

À cet égard, il ne faut pas confondre le concepte de singleton
avec son implémentation dans un langage donné. Un singleton,
c'est un objet dont il ne peut exister qu'une seule instance.
Par définition, et non par hazard -- un autre poster a cité
comme exemple l'écran d'un PC, alors que la plupart des PC ici
sont équipés de deux écrans. Il y a réelement très peu
d'utilisations d'un singleton : les log et les configurations
sont les deux classiques. Encore que ça dépend de
l'application ; si le cahier de charges exige un traitement
unique, ils doivent probablement être des singletons (au sens de
la conception), sinon, ils ne doivent certainement pas l'être.

Travailler avec des instances permet peut-être de mieux
documenter les interactions entre les composants, et quelque
part de clarifier le design.


Oui et non. Si le cahier de charges dit que tous les composants
doivent se comporter de la même façon vis-à-vis de, disons, le
logging, je le vois comme une clarification de *ne pas* passer
un paramètre de type d'objet log -- passer le paramètre suggère
déjà qu'il puisse en avoir plusieurs.

Disons qu'à chaque fois qu'on enregistre ou libère une
référence, les questions de durée de vie ressurgissent, ce qui
arrive moins si on manipule des objets statiques pour lesquels
on n'est pas vraiment responsable. Tout ça est très subjectif,
peut-être que je passe un temps fou à échanger des pointeurs
entre mes composants.


Je repette : ce n'est pas une question de performance, mais de
lisibilité. Passer systèmatiquement trente-six paramètres qui
sont toujours les mêmes, d'après le cahier de charges, ajoute au
bruit, et le rend plus difficile à voir les paramètres qui
varient réelement.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
kanze
Patrick Mézard wrote:

Suite à la dernière itération de la discussion récurrente sur
DCL et les singletons, je me posais la question suivante : le
"Singleton" est-il vraiment utile à quelque chose ? A chaque
fois que j'ai vu ce truc apparaître comme par magie là où on
ne l'attendait pas c'était pour une des raisons suivantes :

1- Ca permet d'avoir des variables globales sous couvert de
respectabilité. Bah oui, c'est dans le légendaire _Design
Patterns_, donc...


Ça permet, effectivement, d'avoir des variables globales. Plus
important, à mon avis, ça permet à s'assurer que tout le monde
se sert de ces variables globales, et non de leur propre
instance. Ce qui fait le singleton, c'est qu'il ne peut y avoir
qu'une seule instance.

2- "C'est cool". C'est le seul motif de conception (encore
merci wikipedia,
<http://fr.wikipedia.org/wiki/Motif_de_conception>) que j'ai
compris ou que j'arrive à utiliser alors je m'en sers. Sinon
j'aurais lu l'autre bouquin pour rien.


Bizarre, bizarre. Parce que parmi les motifs ce conception, ça
en est un qui me sert le moins.

3- "C'est cool et ça en jette de complexité". Y a un chapitre
entier sur le single ultime dans _Modern C++ Design_. Tous les
problèmes sont réglés une fois pour toute : durée de vie,
interdépendance, multi-threading (ah tiens, du DCL !) et j'en
passe. C'est tellement subtil, ça ne peut pas être inutile ?


J'avoue que je ne me suis jamais senti le besoin de quelque
chose de compliqué pour un Singleton. L'intérêt, justement,
c'est de le garder simple.

4- Ca fait beau dans une doc ou sur un schéma de conception.


Dans quel sens. Ça fait beau s'il contribue à la réalisation du
cahier de charges. Sinon...

5- "J'étais pourtant certain qu'il n'y en aurait qu'un !"


Si le cahier de charges exige une centralisation de certaines
fonctionalités... C'est souvent le cas du logging, par exemple.
C'est que tu ne veux pas prendre la risque qu'il puisse en avoir
deux -- tu veux être 100% certain que tout le monde utilise
l'unique instance.

Comme ça, quand on ajoute l'envoie de certains messages à Tivoli
au cahier de charges, tu es sûr qu'ayant ajouter la
configuration à la seule instance, tous les composants sont
affectés.

6- Ca permet de corriger d'autres problèmes de conception.


Ça, c'est plutôt la rôle des fonctions virtuelles et l'héritage
en spaghetti:-).

Quand on trouve un problème quelconque, on corrige la véritable
erreur. S'il s'agit d'une erreur dans la conception, on modifie
la conception, et on répercute les changes vers le bas. Sinon,
au bout de six moins, le programme n'est plus maintenable.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Geoffrey
"Loïc Joly" a écrit dans le message de news:
42daf3e2$0$2514$

2- Il arrive aussi d'utiliser std::cout de n'importe où, particulièrement
pour faire des "traces" (des enregistrements ?). Ce genre d'utilisation
me pose un vrai problème. En pratique, je ne veux pas que l'existence des
enregistreurs impacte le design de l'application ou de la bibliothèque.
Comme ce sont *a priori* des outils de développement et pas des
fonctionnalités, je ne veux pas passer ces instances dans les fonctions,
je dois accèder à des objets globaux. De fait, je ne me souviens pas
avoir vu de bibliothèque de logging sans singleton/globale. Mais Loïc a
peut-être un autre avis sur le sujet :-) ?


Disons que je n'ai rien contre l'utilisation de variables globales pour ce
genre de situation. Le fait d'utiliser un singleton me semblerait par
contre ajouter une contrainte supplémentaire et enlever des
fonctionnalités à l'utilisateur.

Par exemple, pour le logging, si l'objet de log est un singleton, il est
unique. Point. Si c'est juste une variable globale, même si le concepteur
du log ne l'a pas prévu, rien n'empêche l'utilisateur d'avoir 2 log, par
exemple un mode succint et un mode détaillé, dans deux fichiers différents
avec deux objets différents.


Tu peux avoir aussi un gestionnaire de log en Singleton qui gere plusieurs
maniere de logger une Infos ...
tu ne fera qu'un appel, et tu auras tes n logs.


J'ai l'impression que souvent les singletons représentent une limitation
artificielle et arbitraire. Dans beaucoup de cas, cette limitation n'est
pas gênante, mais quand elle le devient... Mettre du log en singleton,
c'est pour moi un peu comme si on décidait qu'un PC n'aurait jamais plus
de 640ko de mémoire.



D'un point de vue de lisibilité du code, le Singleton m'apparait ( ca
n'engage que moi :)) ) plus simple qu'un objet static global ( declaré ou ?
Initialisé a quel endroit ? ).
Je pense plus que la limitation provient de ton objet Singleton et non du
Singleton en lui même ...

--
Loïc




Avatar
kanze
Loïc Joly wrote:

2- Il arrive aussi d'utiliser std::cout de n'importe où,
particulièrement pour faire des "traces" (des
enregistrements ?). Ce genre d'utilisation me pose un vrai
problème. En pratique, je ne veux pas que l'existence des
enregistreurs impacte le design de l'application ou de la
bibliothèque. Comme ce sont *a priori* des outils de
développement et pas des fonctionnalités, je ne veux pas
passer ces instances dans les fonctions, je dois accèder à
des objets globaux. De fait, je ne me souviens pas avoir vu
de bibliothèque de logging sans singleton/globale. Mais Loïc
a peut-être un autre avis sur le sujet :-) ?


Disons que je n'ai rien contre l'utilisation de variables
globales pour ce genre de situation. Le fait d'utiliser un
singleton me semblerait par contre ajouter une contrainte
supplémentaire et enlever des fonctionnalités à l'utilisateur.

Par exemple, pour le logging, si l'objet de log est un
singleton, il est unique. Point. Si c'est juste une variable
globale, même si le concepteur du log ne l'a pas prévu, rien
n'empêche l'utilisateur d'avoir 2 log, par exemple un mode
succint et un mode détaillé, dans deux fichiers différents
avec deux objets différents.


Et si le cahier de charges dit que le log doit être configuré à
partir d'un fichier de configuration ? Comment fais-tu pour
s'assurer que l'utilisateur du deuxième log suit les évolutions
dans le fichier de configuration. (Et est-ce que tu as déjà vu
une application où le log n'était pas configurable ? On n'en a
pas les mêmes besoins lorsqu'une erreur a été constatée que dans
le fonctionnement quotidien.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Loïc Joly wrote:

[...]
2- "C'est cool". C'est le seul motif de conception (encore
merci wikipedia,
<http://fr.wikipedia.org/wiki/Motif_de_conception>) que j'ai
compris ou que j'arrive à utiliser alors je m'en sers. Sinon
j'aurais lu l'autre bouquin pour rien.


C'est un peu le reproche (totalement immérité, je sais) que je
fait à Design Pattern : A une époque, j'ai vu une mode des
design patterns. Si je mets un design pattern (singleton au
hasard) dans mon code, c'est forcément que mon code est bon.


Tu n'inventes pas la roue à chaque ligne de code. Que tu as
entendu parler des Design Patterns ou non, si tu te trouves
devant un problème que tu as déjà résolu dans la passée, tu
utilises la solution qui a marché dans la passée.

En fait, le livre Design Patterns n'a probablement changé rien
dans ta façon d'écrire du code. En revanche, il a dû changer
beaucoup dans ta façon de le documenter. Et ceux qui te suivre
en sauront gré.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Loïc Joly
Loïc Joly wrote:



[...]

2- "C'est cool". C'est le seul motif de conception (encore
merci wikipedia,
<http://fr.wikipedia.org/wiki/Motif_de_conception>) que j'ai
compris ou que j'arrive à utiliser alors je m'en sers. Sinon
j'aurais lu l'autre bouquin pour rien.




C'est un peu le reproche (totalement immérité, je sais) que je
fait à Design Pattern : A une époque, j'ai vu une mode des
design patterns. Si je mets un design pattern (singleton au
hasard) dans mon code, c'est forcément que mon code est bon.



Tu n'inventes pas la roue à chaque ligne de code. Que tu as
entendu parler des Design Patterns ou non, si tu te trouves
devant un problème que tu as déjà résolu dans la passée, tu
utilises la solution qui a marché dans la passée.


Oui, et c'est là le fonctionnement normal. Ce que j'ai vécu, c'est une
dérive perverse. Par exemple, quelqu'un qui décide de créer un singleton
d'un certain type A.

Je lui demande pourquoi il a décidé que A serait un singleton, et sa
réponse n'était pas : Parce-que pour telle ou telle raison, il faut
qu'il n'y en ait qu'une seul instancié, et donc j'ai appliqué le pattern
du singleton. La réponse était du style : Parce que singleton est un
design pattern, et donc mon code est forcément bon.

J'ai vu cette dérive malsaine pour différents patterns et par
différentes personnes.



En fait, le livre Design Patterns n'a probablement changé rien
dans ta façon d'écrire du code. En revanche, il a dû changer
beaucoup dans ta façon de le documenter. Et ceux qui te suivre
en sauront gré.


Il m'a aussi fait cristalliser des points que j'appliquais et
réutilisais sans me rendre compte que je le faisais, et du coup m'a
permis de prendre du recul par rapport à mes choix de conception (tiens,
ce code que j'ai écrit ressemble à tel design pattern, sauf telle
différence. Pourquoi ? Est-ce que la situation était différente, est-ce
que j'ai négligé un élément...). Pour ça, j'ai trouvé la lecture de
Design Pattern très instructive et stimulante.

--
Loïc



Avatar
Gabriel Dos Reis
Loïc Joly writes:

| La réponse était du style : Parce que singleton
| est un design pattern, et donc mon code est forcément bon.

Bah oui, forcément. C'est mathématique.

-- Gaby
Avatar
Stan
"Loïc Joly" a écrit dans le message de news:
42db936b$0$2514$

Je lui demande pourquoi il a décidé que A serait un singleton, et sa
réponse n'était pas : Parce-que pour telle ou telle raison, il faut qu'il
n'y en ait qu'une seul instancié, et donc j'ai appliqué le pattern du
singleton. La réponse était du style : Parce que singleton est un design
pattern, et donc mon code est forcément bon.

J'ai vu cette dérive malsaine pour différents patterns et par différentes
personnes.



C'est sans doute que tes questions les saoulent ;-)
Après il faut admettre qu'il y a des bons programmeurs
qui ne sont pas de très bon pédagogues.


--
-Stan

Avatar
Arnaud Meurgues
wrote:

En fait, le livre Design Patterns n'a probablement changé rien
dans ta façon d'écrire du code.


Toi, peut-être pas. Mais il n'empêche qu'il donne des solutions
auxquelles on n'aurait pas forcément pensé, ou pas pensé du premier coup.
À ce titre, je considère que, pour moi, il a changé ma façon d'écrire du
code. Les visiteurs, les stratégies, les factories ou les décorateurs
sont devenus des architectures de conceptions bien identifiées avant
même d'avoir été en présence des problèmes qu'ils résolvent, ou avant
d'en avoir trouvé une solution générique, réutilisable et identifiable.

En revanche, il a dû changer
beaucoup dans ta façon de le documenter. Et ceux qui te suivre
en sauront gré.


Ça, c'est clair.

Arnaud (qui n'avait pas inventé les design patterns avant d'avoir lu le
bouquin)

1 2 3