Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[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
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...


Ca permet d'avoir aussi des variables globales qu'on peut utiliser
depuis d'autres variables globales sans problème d'ordre d'initialisation.


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.


En définitive, existe-t-il des exemples justifiant l'emploi d'un
Singleton ?


Probablement, mais probablement pas souvent. Même les exemples
classiques me semblent finalement mauvais. Par exemple : Un singleton
peut représenter une chose forcément unique, comme l'écran d'un PC...

Si je devais faire un cours d'introduction rapide sur quelques design
patterns, le singleton n'y figurerais certainement pas.


--
Loïc

Avatar
Arnaud Debaene
Patrick Mézard wrote:
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 ?


Tu oublies le cas le + simple : quand je dois modéliser un objet qui ne peut
exister que de amnière unique dans un système (c'est le but après tout). Au
fait, tu utilises certainement des singletons régulièrement : cin, cout et
cerr par exemple.

Arnaud

Avatar
Patrick Mézard
Loïc Joly wrote:

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...



Ca permet d'avoir aussi des variables globales qu'on peut utiliser
depuis d'autres variables globales sans problème d'ordre d'initialisation.


Du coup, on arrive à une deuxième question, la nécessité d'avoir des
variables globales non-const. Mais ça va faire un peu trop pour
aujourd'hui :-).

--
Patrick


Avatar
Stan
"Patrick Mézard" a écrit dans le message de news:
42da20a5$0$22286$
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 :

[...]

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 ?



Que si on a des doutes sur l'utilité de qq chose,
c'est qu'on en a pas réellement besoin...

--
-Stan

Avatar
Jean-Marc Bourguet
Patrick Mézard writes:

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 ?


Je me demande si en fait tu n'es pas en train de t'attaquer
à toute forme d'état persistant entre appels, que cet état
prenne la forme de variable globale normale, de singleton ou
de variable statique dans des fonctions.

On peut naturellement toujours s'en passer: il suffit de
rendre cet état explicite et de se le faire passer comme
paramètre. On peut même prétendre sans être trop de
mauvaise fois que c'est en prévision pour le moment où cet
état ne pourra plus être partagé. Mais je ne pense pas pour
autant que le faire soit toujours la meilleure solution --
même sans erreurs de conception par ailleurs.

On arrive assez naturellement à des singletons quand on
conçoit son système comme composé de composants
interagissant entre eux. Certains peuvent être
intrinsèquement unique et connu de quasiment tout le monde
(par exemple le composant qui gère l'affichage). Ne pas en
faire un état global poserait des problèmes pratiques
(comment assurer l'initialisation de ces deux composants qui
se connaisse l'un l'autre, ou bien le passe partout, ou bien
on le stocke à des endroits multiples -- et je t'assure
qu'en pratique tous le monde "saura" que tous les moyens d'y
accéder sont équivalents, donc quasiment rien ne sera
facilité s'il faut rendre l'état non unique; j'ai déjà vécu
des cas où un mécanisme d'une généralité excessive avait été
mis en place et être en pratique inutilisable le moment venu
parce que, primo, tout le monde "savait" qu'il n'était pas
utilisé et avait agit avec cette hypothèse et, deuxio, la
généralisation qui était effectivement utile n'avait pas été
prévue).

Un exemple extrême où rendre l'état visible par les
appelants me semble absolument exclu: une fonction dont le
résultat ne dépend que des paramètres, mais coûteuse à
calculer. Comme elle est souvent appelée avec les mêmes
arguments, on décide de cacher le résultat.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Patrick Mézard
Stan wrote:
"Patrick Mézard" a écrit dans le message de news:
42da20a5$0$22286$

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 ?

Qu'en pensez-vous ?

Que si on a des doutes sur l'utilité de qq chose,

c'est qu'on en a pas réellement besoin...


Ou qu'on a fait mieux ou autrement depuis.

--
Patrick


Avatar
Fabien LE LEZ
On Sun, 17 Jul 2005 19:17:07 +0200, Patrick Mézard
:

Du coup, on arrive à une deuxième question, la nécessité d'avoir des
variables globales non-const.


On peut même aller plus loin, et remettre en question la nécessité
d'avoir des langages de programmation ;-p

Avatar
Patrick Mézard
Jean-Marc Bourguet wrote:

Je me demande si en fait tu n'es pas en train de t'attaquer
à toute forme d'état persistant entre appels, que cet état
prenne la forme de variable globale normale, de singleton ou
de variable statique dans des fonctions.


C'est presque ça. En fait, ce qui m'ennuie le plus c'est la difficulté à
cloisonner l'usage de ces objets (variables globales ou singleton
puisque c'est quasiment la même chose). Cela complique beaucoup :

1- La gestion de la durée de vie des instances. Je préfère créer et
détruire ces objets à la main plutôt que compter sur des artifices
parfois complexes pour gèrer ça tout seul.

2- Le réusinage. Il est difficile de savoir qui utilise exactement ces
objets. Les couplages deviennent forts et peu apparents.

3- L'utilisation dans des environnements concurrents. Les techniques
d'accès synchronisés dans des milieux multithreads ou autres dépendent
énormément du type des accès. Il suffit de voir l'énergie dépensée
autour de DCL pour mesurer le coût d'accès totalement libres à un
malheureux pointeur.

On peut naturellement toujours s'en passer: il suffit de
rendre cet état explicite et de se le faire passer comme
paramètre. On peut même prétendre sans être trop de
mauvaise fois que c'est en prévision pour le moment où cet
état ne pourra plus être partagé. Mais je ne pense pas pour
autant que le faire soit toujours la meilleure solution --
même sans erreurs de conception par ailleurs.

On arrive assez naturellement à des singletons quand on
conçoit son système comme composé de composants
interagissant entre eux. Certains peuvent être
intrinsèquement unique et connu de quasiment tout le monde
(par exemple le composant qui gère l'affichage). Ne pas en
faire un état global poserait des problèmes pratiques
(comment assurer l'initialisation de ces deux composants qui
se connaisse l'un l'autre, ou bien le passe partout, ou bien
on le stocke à des endroits multiples -- et je t'assure
qu'en pratique tous le monde "saura" que tous les moyens d'y
accéder sont équivalents, donc quasiment rien ne sera
facilité s'il faut rendre l'état non unique; j'ai déjà vécu
des cas où un mécanisme d'une généralité excessive avait été
mis en place et être en pratique inutilisable le moment venu
parce que, primo, tout le monde "savait" qu'il n'était pas
utilisé et avait agit avec cette hypothèse et, deuxio, la
généralisation qui était effectivement utile n'avait pas été
prévue).


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.

Travailler avec des instances permet peut-être de mieux documenter les
interactions entre les composants, et quelque part de clarifier le
design. 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.

Finalement, c'est la version API du singleton qui m'ennuie le plus.
J'admets bien l'existence d'objets uniques au sein d'un système, mais il
doivent rester peu visibles.

Un exemple extrême où rendre l'état visible par les
appelants me semble absolument exclu: une fonction dont le
résultat ne dépend que des paramètres, mais coûteuse à
calculer. Comme elle est souvent appelée avec les mêmes
arguments, on décide de cacher le résultat.


Je ne comprends pas ton exemple.

--
Patrick

Avatar
Loïc Joly

Tu oublies le cas le + simple : quand je dois modéliser un objet qui ne peut
exister que de amnière unique dans un système (c'est le but après tout). Au
fait, tu utilises certainement des singletons régulièrement : cin, cout et
cerr par exemple.


Depuis quand ce sont des singletons ?

27.3 Standard iostream objects
Header <iostream> synopsis
namespace std {
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
extern wistream wcin;
extern wostream wcout;
extern wostream wcerr;
extern wostream wclog;
}


Rien n'empêche l'utilisateur de créer d'autres objets de type istream,
ostream...

--
Loïc

Avatar
Patrick Mézard
Arnaud Debaene wrote:
Patrick Mézard wrote:

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 ?


Tu oublies le cas le + simple : quand je dois modéliser un objet qui ne peut
exister que de amnière unique dans un système (c'est le but après tout). Au
fait, tu utilises certainement des singletons régulièrement : cin, cout et
cerr par exemple.


Comme le dit Loïc, ce ne sont pas à proprement parler des singletons
mais des variables globales (et il a parfois des problèmes
d'initialisation si on essaye d'y accèder avant le main()). Ceci dit ça
reste un très bon exemple parce qu'il met en valeur les choses suivantes :

1- Ce sont des variables globales qui implémentent des interfaces, en
l'occurence std::istream ou std::ostream, et on les utilise souvent de
façon polymorphique. C'est quand même rare de parler directement à
std::cout. En général on le sélectionne lors d'une phase de
configuration et on le passe à des fonctions prenant du std::ostream. Ce
genre d'usage se rapproche du cas "on crée les instances et on les
distribue aux objets concernés". A la différence d'un motif classique de
singleton, le type est clairement dissocié des instances.

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 :-) ?

Conclusion, avec les contraintes de (2) je ne sais pas me passer de
singletons ou équivalents. Maintenant on peut encore discuter du :

"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".

--
Patrick


1 2 3