web-dev-qa-db-fra.com

Les Singletons sont-ils vraiment si mauvais?

Dupliquer possible:
Quel est le problème avec Singletons?

Il est compréhensible que de nombreux modèles de conception puissent dans certains cas faire l'objet d'abus et, comme maman l'a toujours dit: "Trop de bonnes choses ne sont pas toujours bonnes!"

Je remarque que ces jours-ci, j'utilise beaucoup Singletons et je crains que je puisse abuser moi-même du modèle de conception et que je m'immisce de plus en plus dans une mauvaise habitude.

Nous développons une application Flex dotée d'une structure de données hiérarchique assez grande conservée en mémoire pendant que l'utilisateur y travaille. L'utilisateur peut charger, enregistrer, modifier et actualiser les données à la demande.

Ces données sont centralisées au moyen d’une classe Singleton, qui regroupe plusieurs ArrayCollections, Arrays, objets de valeur et d’autres variables membres natives exposées via des getters et des setters.

Pour obtenir une référence à nos données depuis n’importe où dans l’application, nous effectuons tout le type de méthode de la méthode Model.getInstance (), que tout le monde connaît bien, j'en suis sûr. Cela garantit que nous mettons toujours la main sur la même copie de données, car lors de la conception, nous disions qu'une seule instance était autorisée à exister pendant la durée de vie de l'application.

Par exemple, à partir de ce référentiel de données central, il est facile pour nous, par exemple, de répartir les événements de propriétés modifiées. Plusieurs composants de l'interface utilisateur référençant les données centrales, peuvent ainsi mettre à jour leurs affichages pour refléter les modifications apportées aux données.

Jusqu'à présent, cette approche a été efficace et s'est avérée très pratique dans notre contexte.

Je constate cependant que je suis un peu trop lourd lors de la création de nouvelles classes. Des questions comme celle-ci: une classe doit-elle être un Singleton, ou doit-elle plutôt être gérée d'une autre manière, comme utiliser une usine par exemple, ont parfois tendance à devenir un peu difficiles, avec un peu d'incertitude.

Où puis-je tracer la ligne avec singletons? Existe-t-il une bonne directive pour décider quand utiliser Singletons et quand rester loin d'eux?.

En outre, quelqu'un peut-il recommander un bon livre sur les modèles de conception?

48
josef.van.niekerk

L'essentiel à retenir est que les modèles de conception ne sont qu'un outil pour vous aider à comprendre les concepts abstraits. Une fois que vous avez compris cela, vous limiter spécifiquement à une "recette" d'un livre est inutile et nuit à votre capacité à écrire le code le mieux adapté à vos besoins.

Cela dit, la lecture de livres comme GoF vous proposera plus de moyens de réfléchir aux problèmes afin que, lorsque le moment sera venu de mettre en œuvre quelque chose par vous-même, vous disposerez d'un plus grand nombre de perspectives pour aborder le problème.

Dans votre cas, si utiliser singleton est logique dans tous les cas, continuez. Si cela «convient» et que vous devez l'implémenter d'une manière maladroite, vous devez alors proposer une nouvelle solution. Forcer un motif qui n'est pas parfait, c'est un peu comme marteler une cheville carrée dans un trou rond.

Étant donné que vous dites "cette approche a été efficace et s'est avérée très pratique dans notre contexte", je pense que vous vous en sortez bien.

Voici quelques bons livres:

Gang of Four Book - le livre classique pour les modèles de conception

Head First Design Patterns - J'ai entendu cette recommandation de quelques personnes comme alternative

35
mandaleeka

Oui, les singletons sont mauvais. Ils sont mauvais parce qu'ils ne font que combiner deux propriétés, chacune étant mauvaise environ 95% du temps. (Ce qui voudrait dire qu'en moyenne, les singletons sont mauvais 99,75% du temps;))

Un singleton, tel que défini par le GoF, est une structure de données qui:

  1. Donne un accès global à un objet, et
  2. Indique qu'une seule instance de l'objet peut exister.

Le premier est généralement considéré comme une mauvaise chose. Nous n'aimons pas les globals ... La seconde est un peu plus subtile, mais en général, il n’ya pratiquement pas de cas où il s’agit d’une restriction raisonnable à enforce.

Parfois, il est logique d'avoir une seule instance d'un objet. Dans ce cas, vous choisissez de n'en créer qu'un. Vous n'avez pas besoin d'un singleton pour l'appliquer.

Et généralement, même lorsqu'il est "logique" de n'avoir qu'un seul cas, il s'avère finalement que cela n'a pas de sens. Tôt ou tard, vous aurez besoin de plus d'un enregistreur. Ou plus d'une base de données. Ou vous devrez recréer des ressources pour chacun de vos tests unitaires, ce qui signifie que nous devons être en mesure de les créer à volonté. Cela supprime prématurément la flexibilité de notre code avant que nous en comprenions les conséquences.

Les singletons masquent les dépendances et augmentent le couplage (chaque classe peut potentiellement dépendre d'un singleton, ce qui signifie que la classe ne peut pas être réutilisée dans d'autres projets à moins que nous réutilisions également tous nos singletons), et parce que ces dépendances ne sont pas immédiatement visibles (en tant que paramètres de fonction/constructeur ), nous ne les remarquons pas et nous n'y pensons généralement pas lorsque nous les créons. Il est si facile de tirer dans un singleton, il agit presque comme une variable locale et tout, alors nous avons tendance à les utiliser beaucoup une fois qu'ils sont là. Et cela les rend presque impossible à enlever à nouveau. Vous vous retrouvez peut-être pas avec du code spaghetti, mais avec des graphiques de dépendance spaghetti. Et tôt ou tard, vos dépendances incontrôlables signifieront que les singletons commenceront à dépendre les uns des autres, puis vous obtiendrez des dépendances circulaires lorsqu’une tentative d’initialisation sera lancée.

Ils rendent extrêmement difficile les tests unitaires. (Comment testez-vous une fonction qui appelle des fonctions sur un objet singleton? Nous ne voulons pas que le code singleton réel soit exercé, mais comment pouvons-nous empêcher cela?

Oui, les singletons sont mauvais.

Parfois, vous voulez vraiment un global. Ensuite, utilisez un global, pas un singleton.

Parfois, très, très très rarement, vous pouvez avoir une situation dans laquelle la création de plusieurs instances d’une classe est une erreur, où elle ne peut pas être effectuée sans provoquer d’erreurs. (Le seul cas auquel je puisse penser, et même que cela soit artificiel, est que vous représentiez un périphérique matériel. Vous n’avez qu’un seul processeur graphique, donc si vous voulez le mapper sur un objet de votre code, il logique qu’une seule instance puisse exister). Mais si vous vous trouvez dans une telle situation (et encore une fois, pour souligner une situation où plusieurs instances entraînent de graves erreurs, et pas simplement une situation dans laquelle "je ne peux penser à aucun cas d'utilisation pour plus d'une instance"), alors appliquez cette contrainte, mais faites-le sans rendre également l'objet globalement visible.

Chacune de ces deux propriétés peut être utile, dans de rares cas. Mais je ne peux pas penser à un seul cas où la combinaison serait une bonne chose.

Malheureusement, beaucoup de gens ont eu l’idée que "les singletons sont des globaux conformes à la POO". Non, ils ne sont pas. Ils souffrent toujours des mêmes problèmes que les globals, en plus d’en introduire d’autres, qui n’ont aucun rapport. Il n'y a absolument aucune raison de préférer un singleton à un simple vieux monde.

109
jalf

Les développeurs de logiciels semblent assez bien divisés en deux camps, selon qu’ils préfèrent un style de codage idéaliste ou pragmatique:

  • Idéaliste: Jamais utilisez le motif singleton. 
  • Pragmatique: Évitez le motif singleton.

Personnellement, je privilégie l'approche pragmatique. Parfois, il est judicieux d'enfreindre les règles, mais uniquement si vous comprenez vraiment ce que vous faites et si vous êtes prêt à accepter les risques associés. Si vous pouvez répondre «oui» aux questions ci-dessous concernant votre cas d'utilisation spécifique, le modèle de singleton peut générer des avantages pratiques. 

  • Le singleton est-il externe à votre application? Les bases de données, les services de mise en file d'attente et les ESB sont tous des exemples de macro parfaitement valides du modèle singleton.
  • KISS: Est-ce que toute votre application est limitée à 2-3 singletons internes?
  • DRY: Ces singletons sont-ils par nature globaux et impliqueraient donc de devoir insérer des références dans presque tous les objets de votre application? (par exemple, un enregistreur ou un médiateur de composant)? 
  • Vos singletons ne dépendent-ils que l'un de l'autre et/ou de l'environnement d'exploitation?
  • Vous êtes-vous assuré de la séquence de démarrage et d'arrêt appropriée pour chaque singleton, y compris des considérations relatives à la gestion de la mémoire? Par exemple, un pool de threads de style "Grand Central" peut avoir besoin d'inclure les méthodes instance Run () et Shutdown () dans main () pour que l'exécution des tâches ne soit garantie que lorsque les objets sur lesquels elles fonctionnent sont dans un état valide.
14
kgriffs

Les singletons ne tuent pas les programmes, les programmeurs tuent les programmes.

Comme pour toute construction de programmation, lorsque vous l'utilisez correctement, vous ne vous tirerez pas une balle dans le pied.

Les livres recommandés sont corrects, mais ils ne donnent pas toujours suffisamment de connaissances en ce qui concerne l’expérience du moment où vous pourriez choisir d’utiliser Singleton.

Cette expérience ne survient que lorsque vous avez découvert que Singleton est un mauvais choix lorsque vous devez disposer de plusieurs instances. Soudainement, vous avez beaucoup de difficulté à injecter les références d'objet partout.

Parfois, il est préférable d’aller de l’avant et d’avoir les références d’objet en place, mais le fait que vous utilisiez Singleton aide vraiment à identifier l’ampleur du problème auquel vous auriez à faire face si vous deviez le refactoriser selon un modèle différent. Ce qui, à mon avis, est une très bonne chose: le fait d’avoir une classe (même si elle est mal conçue) donne une certaine capacité à voir les effets d’un changement de classe.

9
Cade Roux

Nous avons démarré un projet où nous sommes fondamentalement confrontés à la même question, à savoir comment accéder au modèle, et en particulier à son élément racine. Le projet n'est pas une application Flex, mais un jeu! application Web, mais cela n'a pas d'importance.

Avoir un objet unique unique dans le système convient, le problème est comment y accéder. Le débat sur singleton est donc lié à la notion de dependency injection (DI) et à la manière d’obtenir des objets. 

Les principaux arguments pour DI sont les suivants:

  • testabilité et moqueries
  • dissocier l'instanciation d'un objet de l'utilisation (ce qui peut conduire à une gestion du cycle de vie)
  • séparation des préoccupations

Les approches possibles pour DI sont (voir le classique article de Fowler):

  • passer l'objet dans les paramètres de la méthode
  • localisateur de service
  • Cadre DI

Dans cette perspective, le modèle singleton est simplement une sorte de localisateur de service, par exemple. Model.getInstance().

Mais pour offrir une flexibilité maximale face aux modifications futures, la référence à l'objet unique doit être transmise autour de autant que possible, et obtenue avec Model.getInstance() uniquement lorsque cela est nécessaire. Cela donnera aussi un code plus propre.

4
ewernli

À mon avis, l’utilisation de Singletons signale directement un défaut de conception. La raison en est simplement qu'elles permettent de contourner les mécanismes normaux de création et de destruction d'objets intégrés au C++. Si un objet a besoin d'une référence à un autre objet, il doit lui transmettre une référence lors de la construction ou en créer une nouvelle instance en interne. Mais lorsque vous utilisez un singleton, vous obscurcissez explicitement le cycle de création et de démontage. Un problème connexe est qu’il est extrêmement difficile de contrôler la durée de vie d’un singleton. En conséquence, de nombreux packages qui incluent des implémentations génériques de singleton incluent également des gestionnaires de durée de vie d’objets encombrants, etc. Parfois, je me demande si ceux-ci n'existent pas simplement pour gérer les singletons. 

Fondamentalement, si vous devez utiliser un objet à plusieurs endroits, il doit être créé explicitement au point commun le plus élevé de la pile, puis transmis via une référence à tous ceux qui l'utilisent. Parfois, les gens utilisent Singletons parce qu'ils ont des problèmes pour transmettre plusieurs arguments à de nouveaux threads, mais ne tombez pas dans cette éventualité, définissez explicitement vos arguments de thread et transmettez-les au nouveau thread de la même manière. Vous constaterez que votre programme est bien plus propre et qu'il n'y a pas de mauvaise surprise en raison de dépendances d'initialisation statiques ou d'un démontage erroné.

3
Andy B

Je sais que c’est un vieux fil conducteur, mais personne ne semble mentionner le schéma qui correspond à ce que le PO essayait de faire. Ce que je crois qu’il décrit un besoin est appelé le Modèle de médiateur . SourceMaking est un site fantastique pour apprendre/référencer ce type d'informations. Définitivement mon lieu de prédilection pour initier les gens aux modèles de logiciels. En outre, il est généralement judicieux de ne pas souscrire à l'idée que tout modèle de conception est nécessairement intrinsèquement bon ou mauvais. Ils ont tous leur utilité, ce n’est que l’astuce de savoir quand et où les utiliser. Les personnes qui déclarent ne jamais utiliser de singletons ne comprennent pas leur utilité. 

2
tnicks

Singletons ne sont certainement pas mauvais. Ils ont leurs utilisations, certaines très bonnes. Les développeurs inexpérimentés ont tendance à trop utiliser les singletons, car ce sont les premiers motifs de conception sur lesquels ils se familiarisent. C'est assez simple, ils le jettent donc partout sans réfléchir aux implications.

Chaque fois que vous souhaitez utiliser un singleton, essayez de comprendre pourquoi vous le faites et quels sont les avantages et les inconvénients de l’utilisation de ce modèle.

Les singletons créent effectivement un ensemble global accessible de «choses» (soit des données, soit des méthodes) et je pense que la plupart des gens conviendraient qu'utiliser trop de variables globales n'est pas une bonne idée. L'intérêt des classes et de l'orientation des objets est de regrouper les objets en zones distinctes plutôt que de tout regrouper dans un espace global gigantesque.

L'un des «modèles» que j'ai tendance à préférer aux singletons est de transmettre les objets nécessaires par le haut. Je les crée une fois au cours de la phase d'initialisation de mes applications et les transmet à travers tous les objets qui doivent y accéder. Il imite la partie "création unique" d'un motif singleton, mais sans la partie "globale".

L'intérêt d'un singleton est qu'il ne concerne que les objets où il ne devrait en exister qu'un. Vous mentionnez un ensemble de classes de contrôle de données. Peut-être qu’en réalité, il peut arriver qu’une application veuille créer deux ensembles de classes de contrôle de données, de sorte qu’appliquer un singleton sur ce point n’est peut-être pas tout à fait correct. Au lieu de cela, si vous avez créé ces classes de données sur app init et que vous les avez transmises, vous ne créeriez qu'un jeu car c'est ce que votre application actuelle requiert, mais vous laissez la possibilité ouverte, à un moment donné, si vous avez besoin d'un second jeu vous pouvez facilement les créer. En outre, les classes de contrôle de données doivent-elles réellement être accessibles de manière globale de n’importe où dans l’application. Je pense que non, mais ils devraient probablement être uniquement accessibles à partir d’une couche d’accès aux données de niveau inférieur.

Certaines personnes ont recommandé le livre GOF. Je dirais que oui, c’est un excellent livre, mais essayez d’abord de trouver un livre sur l’architecture générale, en particulier sur la conception 2/3/n-tier, l’encapsulation, l’abstraction et ce genre de principes. Cela vous donnera une base plus solide pour comprendre l'utilisation appropriée des modèles dont parle le GOF.

[Edit: L'autre fois qu'une variante singleton peut être utile, c'est quand vous voulez un seul point d'accès à quelque chose, mais le détail de l'implémentation peut en réalité être plus qu'une chose. L'appelant n'a pas besoin de savoir que sous la couverture, sa demande d'objet singleton est en fait résolue par rapport à plusieurs objets disponibles et qu'un est renvoyé. Je pense à quelque chose comme un pool de threads ici, où l'utilisation va, hé, juste me donner un thread, j'ai besoin de 1, mais je me fiche de celui qui]

2
Simon P Stevens

Google semble être convaincu que les singletons sont une mauvaise idée. 

Cela ne veut pas dire que tout ce que Google fait est parfait ou que chaque opinion est la fin de tout argument, mais ils sont allés jusqu'à écrire ce détecteur Singleton pour les extirper. Forge ta propre opinion.

0
duffymo

Non, ils ne sont pas nécessairement mauvais.

Comme pour un livre, vous devez commencer par les classiques .

0
Stu

Les singletons ne sont pas "si mauvais". Si vous avez beaucoup de singletons liés et que vous pouvez remplacer/consolider un certain nombre d’entre eux en utilisant une usine, sans perdre quoi que ce soit qui vous tient à cœur, vous devez le faire.

En ce qui concerne les livres, bon, il y a une sorte de Canon .

0
chaos
0
Jeff Meatball Yang