La variable globale glorifiée - devient une classe mondiale glorifiée. Certains disent que la conception orientée objet est révolutionnaire.
Donnez-moi des scénarios, autres que le bon vieux logger où il est logique d'utiliser le singleton.
La variable globale glorifiée - devient une classe mondiale glorifiée. Certains disent que la conception orientée objet est révolutionnaire.
Donnez-moi des scénarios, autres que le bon vieux logger où il est logique d'utiliser le singleton.
Réponses:
Dans ma quête de la vérité, j'ai découvert qu'il y a en fait très peu de raisons "acceptables" d'utiliser un Singleton.
Une raison qui revient souvent sur les internets est celle d'une classe de «journalisation» (que vous avez mentionnée). Dans ce cas, un Singleton peut être utilisé à la place d'une seule instance d'une classe car une classe de journalisation doit généralement être utilisée encore et encore ad nauseam par chaque classe d'un projet. Si chaque classe utilise cette classe de journalisation, l'injection de dépendances devient lourde.
La journalisation est un exemple spécifique d'un singleton "acceptable" car il n'affecte pas l'exécution de votre code. Désactivez la journalisation, l'exécution du code reste la même. Activez-le, même même. Misko le présente de la manière suivante dans Root Cause of Singletons : "Les informations circulent ici dans un sens: de votre application vers le logger. Même si les loggers sont à l'état global, car aucune information ne circule des loggers vers votre application, les loggers sont acceptables."
Je suis sûr qu'il existe également d'autres raisons valables. Alex Miller, dans " Patterns I Hate ", parle de localisateurs de services et d'interfaces utilisateur côté client étant également des choix "acceptables".
Pour en savoir plus sur Singleton, je vous aime, mais vous m'abaissez.
Un candidat Singleton doit satisfaire à trois exigences:
Si votre Singleton proposé n'a qu'une ou deux de ces exigences, une refonte est presque toujours la bonne option.
Par exemple, il est peu probable qu'un spouleur d'imprimante soit appelé à partir de plusieurs emplacements (le menu Imprimer), vous pouvez donc utiliser des mutex pour résoudre le problème d'accès simultané.
Un enregistreur simple est l'exemple le plus évident d'un Singleton éventuellement valide, mais cela peut changer avec des schémas d'enregistrement plus complexes.
Lire les fichiers de configuration qui ne doivent être lus qu'au démarrage et les encapsuler dans un Singleton.
Properties.Settings.Default
.NET.
Vous utilisez un singleton lorsque vous devez gérer une ressource partagée. Par exemple, un spouleur d'imprimante. Votre application ne doit avoir qu'une seule instance du spouleur afin d'éviter toute demande conflictuelle pour la même ressource.
Ou une connexion à une base de données ou un gestionnaire de fichiers, etc.
Les singletons en lecture seule stockant un état global (langue utilisateur, chemin d'accès au fichier d'aide, chemin d'application) sont raisonnables. Attention à utiliser des singletons pour contrôler la logique métier - le célibataire finit presque toujours par être multiple
Gestion d'une connexion (ou d'un pool de connexions) à une base de données.
Je l'utiliserais également pour récupérer et stocker des informations sur des fichiers de configuration externes.
L'une des façons d'utiliser un singleton est de couvrir une instance où il doit y avoir un seul "courtier" contrôlant l'accès à une ressource. Les singletons sont bons dans les enregistreurs car ils assurent l'accès à, disons, un fichier, qui ne peut être écrit qu'en exclusivité. Pour quelque chose comme la journalisation, ils fournissent un moyen d'abstraire les écritures dans quelque chose comme un fichier journal - vous pouvez envelopper un mécanisme de mise en cache dans votre singleton, etc.
Pensez également à une situation où vous avez une application avec de nombreuses fenêtres / threads / etc, mais qui nécessite un seul point de communication. Une fois, j'en ai utilisé un pour contrôler les travaux que je voulais lancer mon application. Le singleton était responsable de la sérialisation des travaux et de l'affichage de leur statut à toute autre partie du programme qui était intéressée. Dans ce genre de scénario, vous pouvez considérer un singleton comme une sorte de classe "serveur" s'exécutant dans votre application ... HTH
Un singleton doit être utilisé lors de la gestion de l'accès à une ressource partagée par l'ensemble de l'application, et il serait destructeur d'avoir potentiellement plusieurs instances de la même classe. S'assurer que l'accès aux ressources partagées en toute sécurité est un très bon exemple de cas où ce type de modèle peut être vital.
Lorsque vous utilisez Singletons, vous devez vous assurer que vous ne cachez pas accidentellement les dépendances. Idéalement, les singletons (comme la plupart des variables statiques dans une application) doivent être configurés pendant l'exécution de votre code d'initialisation pour l'application (static void Main () pour les exécutables C #, static void main () pour les exécutables java), puis transmis à toutes les autres classes instanciées qui en ont besoin. Cela vous aide à maintenir la testabilité.
Je pense que l'utilisation de singleton peut être considérée comme la même chose que la relation plusieurs-à-un dans les bases de données. Si vous avez de nombreuses parties différentes de votre code qui doivent fonctionner avec une seule instance d'un objet, c'est là qu'il est logique d'utiliser des singletons.
Un exemple pratique d'un singleton peut être trouvé dans Test :: Builder , la classe qui sauvegarde à peu près tous les modules de test Perl modernes. Le test :: Builder singleton stocke et organise l'état et l'historique du processus de test (résultats de test historiques, compte le nombre de tests exécutés) ainsi que des éléments comme la destination de la sortie de test. Ils sont tous nécessaires pour coordonner plusieurs modules de test, écrits par différents auteurs, pour travailler ensemble dans un seul script de test.
L'histoire du singleton Test :: Builder est pédagogique. L'appel new()
vous donne toujours le même objet. Tout d'abord, toutes les données ont été stockées en tant que variables de classe sans rien dans l'objet lui-même. Cela a fonctionné jusqu'à ce que je veuille tester Test :: Builder avec lui-même. Ensuite, j'avais besoin de deux objets Test :: Builder, un configuré comme un mannequin, pour capturer et tester son comportement et sa sortie, et un pour être le véritable objet de test. À ce stade, Test :: Builder a été refactorisé en un objet réel. L'objet singleton était stocké en tant que données de classe et le new()
renvoyait toujours. create()
a été ajouté pour créer un nouvel objet et activer les tests.
Actuellement, les utilisateurs souhaitent modifier certains comportements de Test :: Builder dans leur propre module, mais en laisser d'autres, tandis que l'historique des tests reste commun à tous les modules de test. Ce qui se passe maintenant, c'est que l'objet Test :: Builder monolithique est décomposé en morceaux plus petits (historique, sortie, format ...) avec une instance Test :: Builder les rassemblant. Maintenant, Test :: Builder ne doit plus être un singleton. Ses composants, comme l'histoire, peuvent l'être. Cela pousse la nécessité inflexible d'un singleton à un niveau inférieur. Il donne plus de flexibilité à l'utilisateur pour mélanger et assortir les morceaux. Les objets singleton plus petits peuvent désormais simplement stocker des données, avec leurs objets conteneurs qui décident comment les utiliser. Il permet même à une classe non-Test :: Builder de jouer en utilisant l'historique Test :: Builder et les singletons de sortie.
Il semble y avoir un push and pull entre la coordination des données et la flexibilité du comportement qui peut être atténuée en mettant le singleton autour des données partagées avec le plus petit nombre de comportements possible pour garantir l'intégrité des données.
Lorsque vous chargez un objet Propriétés de configuration, à partir de la base de données ou d'un fichier, il est utile de l'avoir comme singleton; il n'y a aucune raison de continuer à relire les données statiques qui ne changeront pas pendant le fonctionnement du serveur.
Vous pouvez utiliser Singleton lors de l'implémentation du modèle d'état (de la manière indiquée dans le livre GoF). En effet, les classes d'état concrètes n'ont pas d'état propre et exécutent leurs actions en termes de classe de contexte.
Vous pouvez également faire de Abstract Factory un singleton.
setState()
responsable de décider de la politique de création d'état. Cela aide si votre langage de programmation prend en charge les modèles ou les génériques. Au lieu de Singleton, vous pouvez utiliser le modèle Monostate , où l'instanciation d'un objet d'état finit par réutiliser le même objet d'état global / statique. La syntaxe pour changer l'état peut rester inchangée, car vos utilisateurs n'ont pas besoin de savoir que l'état instancié est un Monostate.
Ressources partagées. Surtout en PHP, une classe de base de données, une classe de modèle et une classe de dépôt de variable globale. Tous doivent être partagés par tous les modules / classes utilisés dans le code.
C'est une véritable utilisation d'objet -> la classe de modèle contient le modèle de page en cours de création, et il est façonné, ajouté, modifié par les modules qui s'ajoutent à la sortie de la page. Il doit être conservé comme une seule instance afin que cela puisse se produire, et il en va de même pour les bases de données. Avec un singleton de base de données partagée, toutes les classes de modules peuvent accéder aux requêtes et les obtenir sans avoir à les réexécuter.
Un dépôt de variable global singleton vous fournit un dépôt de variable global, fiable et facilement utilisable. Cela nettoie beaucoup votre code. Imaginez avoir toutes les valeurs de configuration dans un tableau dans un singleton comme:
$gb->config['hostname']
ou avoir toutes les valeurs de langue dans un tableau comme:
$gb->lang['ENTER_USER']
À la fin de l'exécution du code de la page, vous obtenez, disons, une version désormais mature:
$template
Singleton, un $gb
singleton qui a le tableau lang pour le remplacer, et toutes les sorties sont chargées et prêtes. Il vous suffit de les remplacer par les clés qui sont désormais présentes dans la valeur de page de l'objet modèle mature, puis de les diffuser à l'utilisateur.
Le grand avantage de ceci est que vous pouvez faire N'IMPORTE QUEL post-traitement que vous aimez sur n'importe quoi. Vous pouvez diriger toutes les valeurs de langue vers google translate ou un autre service de traduction et les récupérer et les replacer à leur place, traduites, par exemple. ou, vous pouvez remplacer dans les structures de page, ou, les chaînes de contenu, comme vous le souhaitez.
Il peut être très pragmatique de configurer des problèmes d'infrastructure spécifiques en tant que singletons ou variables globales. Mon exemple préféré est celui des frameworks d'injection de dépendance qui utilisent des singletons pour servir de point de connexion au framework.
Dans ce cas, vous dépendez de l'infrastructure pour simplifier l'utilisation de la bibliothèque et éviter une complexité inutile.
Je l'utilise pour un objet encapsulant des paramètres de ligne de commande lorsqu'il s'agit de modules enfichables. Le programme principal ne sait pas quels sont les paramètres de ligne de commande pour les modules qui sont chargés (et ne sait pas toujours quels modules sont chargés). par exemple, charge principale A, qui n'a pas besoin de paramètres lui-même (alors pourquoi cela devrait prendre un pointeur / référence / autre, je ne suis pas sûr - ressemble à de la pollution), puis charge les modules X, Y et Z. Deux de ceux-ci, disons X et Z, ont besoin (ou acceptent) de paramètres, donc ils rappellent au singleton de ligne de commande pour lui dire quels paramètres accepter, et à l'exécution ils rappellent pour savoir si l'utilisateur a réellement spécifié d'eux.
À bien des égards, un singleton pour gérer les paramètres CGI fonctionnerait de manière similaire si vous n'utilisez qu'un seul processus par requête (les autres méthodes mod_ * ne le font pas, donc ce serait mauvais là-bas - donc l'argument qui dit que vous ne devriez pas '' t utilisez des singletons dans le monde mod_cgi au cas où vous porteriez vers le mod_perl ou autre monde).
Un exemple avec du code, peut-être.
Ici, ConcreteRegistry est un singleton dans un jeu de poker qui permet aux comportements tout au long de l'arborescence des packages d'accéder aux quelques interfaces principales du jeu (c'est-à-dire les façades du modèle, de la vue, du contrôleur, de l'environnement, etc.):
http://www.edmundkirwan.com/servlet/fractal/cs1/frac-cs40.html
Ed.
1 - Un commentaire sur la première réponse:
Je ne suis pas d'accord avec une classe Logger statique. cela peut être pratique pour une implémentation, mais il ne peut pas être remplacé pour des tests unitaires. Une classe statique ne peut pas être remplacée par un double test. Si vous ne faites pas de test unitaire, vous ne verrez pas le problème ici.
2 - J'essaie de ne pas créer un singleton à la main. Je viens de créer un objet simple avec des constructeurs qui me permettent d'injecter des collaborateurs dans l'objet. Si j'avais besoin d'un singleton, j'utiliserais un framework d'inyection de dépendances (Spring.NET, Unity pour .NET, Spring pour Java), ou un autre.
ILogger logger = Logger.SingleInstance();
lorsque cette méthode est statique et renvoie une instance stockée de manière statique d'un ILogger. Vous avez utilisé l'exemple d'un «framework d'injection de dépendances». Presque tous les conteneurs DI sont des singletons; leurs configurations sont définies de façon statique et finalement accessibles à partir / stockées dans une interface de fournisseur de services unique.