46 votes

Refactoring Singleton Overuse

Aujourd'hui j'ai eu une révélation, et c'était que je faisais tout de travers. Un peu d'histoire: j'ai hérité d'une application en C#, ce qui était vraiment juste une collection de méthodes statiques, une procédure gâchis de code C#. J'ai refait ce le mieux que je connaissais à l'époque, apportant beaucoup de post-collège de la connaissance de la programmation orientée objet. Pour rendre une longue histoire courte, bon nombre d'entités dans le code ont s'est avéré être des Singletons.

Aujourd'hui j'en ai besoin de 3 nouvelles classes, qui devraient tous suivre le même pattern Singleton pour correspondre au reste du logiciel. Si je continue à dégringoler cette pente glissante, finalement, chaque classe dans mon application sera Singleton, qui va vraiment pas être logiquement différent de l'original groupe de méthodes statiques.

J'ai besoin d'aide sur la façon de repenser à cela. Je sais à propos de l'Injection de Dépendance, et qui serait généralement être la stratégie à utiliser pour briser le Singleton malédiction. Cependant, j'ai quelques questions spécifiques liées à cette factorisation, et tous sur les meilleures pratiques pour le faire.

  1. Comment acceptable est l'utilisation de variables statiques pour encapsuler les informations de configuration? J'ai un cerveau bloc sur l'utilisation de la statique, et je pense que c'est dû à un début de OO classe dans un collège où le professeur a dit statique a été mauvais. Mais, devrais-je avoir à reconfigurer la classe à chaque fois que je y avoir accès? Lors de l'accès à du matériel, est-il acceptable de laisser un pointeur statique pour les adresses et les variables nécessaires, ou devrais-je effectuer constamment Open() et Close() des opérations?

  2. Droit maintenant, j'ai une méthode unique, agissant en tant que contrôleur. Plus précisément, je ne cesse de sondage à plusieurs instruments externes (via les pilotes de matériel) pour les données. Ce type de contrôleur être la voie à suivre, ou devrais-je spawn des threads séparés pour chaque instrument au programme de démarrage? Dans ce dernier cas, comment puis-je faire de cette orientée objet? Dois-je créer des classes appelées InstrumentAListener et InstrumentBListener? Ou est-il une manière standard de cette approche?

  3. Est-il une meilleure façon de faire de la configuration globale? Maintenant je n'ai qu' Configuration.Instance.Foo saupoudré généreusement tout au long du code. Presque chaque classe qui l'utilise, donc peut-être le garder comme un Singleton de sens. Toutes les pensées?

  4. Beaucoup de mes classes sont des choses comme SerialPortWriter ou DataFileWriter, qui doivent s'asseoir autour d'attente pour ce flux des données. Depuis qu'ils sont actifs tout le temps, comment dois-je faire pour écouter les événements générés lorsque des données?

Toutes autres ressources, des livres, ou des commentaires sur la façon de sortir de Singletons et d'autres modèle de surconsommation serait utile.

13voto

Aren Points 17843

Bon, voici mon meilleur coup à attaquer à cette question:

(1) la Statique

Le Problème avec static que vous pouvez avoir, c'est qu'il signifie différentes choses .NET et C++. Statique signifie qu'il est accessible sur la classe elle-même. Pour ce qui est de l'acceptabilité de l'id de dire que c'est plus quelque chose que vous souhaitez utiliser pour faire de la non-occurrence des opérations spécifiques sur une classe. Ou quelques choses comme Math.Abs(...). Ce que vous devriez utiliser pour une config est probablement une manière statique accédé à la propriété pour la tenue de l'actuel/configuration active. Aussi peut-être certaines classes statiques pour le chargement/sauvegarde le réglage de la config, cependant la config devrait être un Objet de sorte qu'il peut être passé autour manipulé, etc. public class MyConfiguration { public const string DefaultConfigPath = "./config.xml";

  protected static MyConfiguration _current;
  public static MyConfiguration Current
  {
    get
    {
      if (_current == null)
        Load(DefaultConfigPath);
      return _current;
    }
  }

  public static MyConfiguration Load(string path)
  {
    // Do your loading here
    _current = loadedConfig;
    return loadedConfig; 
  }

  // Static save function

  //*********** Non-Static Members *********//

  public string MyVariable { get; set; }
  // etc..
}

(2) Contrôleur/Matériel

Vous devriez probablement regarder dans une approche réactive, IObserver<> ou IObservable<>, c'est une partie de la Réactif Cadre (Rx).

Une autre approche est d'utiliser un pool de threads pour planifier votre bureau de vote de tâches, que vous pouvez obtenir un grand nombre de threads si vous avez beaucoup de matériel pour piscine. Veuillez vous assurer, avant d'utiliser tout type de Filetage à apprendre beaucoup de choses sur elle. Il est très facile de faire des erreurs, vous pouvez même ne pas réaliser. Ce Livre est une excellente source et vous apprendra beaucoup.

De toute façon, vous devriez probablement construire des services (juste un nom vraiment) pour la gestion de votre matériel qui sont responsables de la collecte d'informations sur un service (essentiellement un modèle-modèle). À partir de là, votre contrôleur central peut les utiliser pour accéder aux données en gardant la logique de programme dans le contrôleur, et le matériel de logique dans le service.

(3) Configuration Globale

J'ai peut-être touché à ce sujet dans le point n ° 1 mais en général, c'est là où nous allons, si vous vous trouvez en tapant trop beaucoup, vous pouvez toujours sortir de là en supposant que l' .Instance est un objet.

MyConfiguration cfg = MyConfiguration.Current
cfg.Foo // etc...

(4) à l'Écoute des données

De nouveau le réactif cadre pourrait vous aider, ou vous pourriez créer un event-driven modèle qui utilise des déclencheurs pour les données entrantes. Ce sera assurez-vous que vous n'êtes pas de blocage sur un fil jusqu'données. Il permet de réduire la complexité de votre application grandement.

4voto

Keith Nicholas Points 20875

pour commencer, vous pouvez limiter l'utilisation de singleton à travers le "Registre", ce qui signifie que vous avez un singleton qui vous permet d'obtenir un tas d'autres objets préconfigurés.

Ce n'est pas une "solution", mais une amélioration, il fait de nombreux objets qui sont des singletons un peu plus normal et testable. par exemple... (totalement artificiel exemple)

HardwareRegistry.SerialPorts.Serial1.Send("blah");

mais le vrai problème semble être, vous êtes aux prises avec un ensemble d'objets qui fonctionnent bien ensemble. Il y a deux étapes dans OO.... configuration des objets, et de laisser les objets font leur chose.

donc peut-être regarder comment vous pouvez configurer la non singleton objets à travailler ensemble, et puis l'accrocher que d'un registre.

Statique :-

Beaucoup d'exceptions à la règles ici, mais en général, l'éviter, mais il est utile pour faire des singletons, et de créer des méthodes qui ne "général" de l'informatique en dehors du contexte d'un objet. ( comme pour les Mathématiques.Min )

Les Données De Surveillance De :-

son souvent mieux à faire que de vous suggérer, créer un thread avec un tas de préconfiguré objets qui feront de votre suivi. L'utilisation de passage de messages de communication entre les threads (par le biais d'un thread-safe file d'attente) afin de limiter le thread problèmes de verrouillage. Utiliser le registre de modèle pour accéder à des ressources matérielles.

vous voulez quelque chose comme une InstrumentListner qui utilise un InstrumentProtocol (que vous sous-classe pour chaque protocole) je ne sais pas, LogData. Le modèle de commande peut être utile ici.

Configuration:-

avoir vos informations de configuration et d'utiliser quelque chose comme le "constructeur" de modèle pour traduire votre configuration dans un ensemble d'objets mis en place de façon particulière. c'est à dire, ne faites pas de vos classes conscient de configuration, de faire un objet qui permet de configurer des objets d'une manière particulière.

Les Ports Série :-

Je fais un tas de travailler avec ces, ce que j'ai est une connexion en série, ce qui génère un flux de caractères qui, il l'envoie comme un événement. Alors j'ai quelque chose qui interprète le flux du protocole d'importance des commandes. Mes classes de protocoles de travail avec un générique "IConnection" dont une SerialConnection hérite..... J'ai aussi TcpConnections, MockConnections, etc, pour être en mesure d'injecter des données d'essai, tuyaux ou des ports série à partir d'un ordinateur à un autre, etc. Donc les classes de protocoles juste interprétation d'un ruisseau, d'avoir une statemachine, et l'expédition des commandes. Le protocole est préconfiguré avec une Connexion, Diverses choses enregistré avec le protocole, ainsi lorsque l'on a des données significatives ils seront déclenchées et faire leur chose. Tout cela est construit à partir d'une configuration au début, ou reconstruite à la volée si quelque chose change.

1voto

TrueWill Points 14855

Depuis que vous connaissez l’injection de dépendance, avez-vous envisagé d’utiliser un conteneur IoC pour gérer les durées de vie? Voir ma réponse à une question sur les classes statiques.

1voto

TerryP Points 737
  1. Vous (l'OP) semblent préoccupés par OO design, eh bien, je vais le mettre de cette façon, lorsque la réflexion sur les variables statiques choses. Le concept de base est l'encapsulation et de la réutilisation; il y a des choses que vous ne s'intéressent pas à réutiliser, mais vous veulent presque toujours l'encapsulation. Si c'est une variable statique, il n'est pas vraiment encapsulé, est-il? Pensez à qui en a besoin pour y accéder, pourquoi, et dans quelle mesure vous pouvez les MASQUER, depuis le code client. De bonnes conceptions peuvent souvent changer leurs internes sans trop de casse pour les clients, c'est ce que vous voulez penser au sujet de. Je suis d'accord avec Scott Meyers (Effective C++) à propos de beaucoup de choses. La programmation orientée objet va au-delà du mot-clé class. Si vous n'avez jamais entendu parler de cela, chercher des propriétés: oui, ils peuvent être statiques, et C# est une très bonne façon de les utiliser. Contrairement à littéralement à l'aide d'une statique de la variable. Comme je l'ai déjà évoqué au début de cet élément de la liste: pensez à la façon de ne pas se tirer dans le pied, plus tard, comme les changements de classe avec le temps, c'est quelque chose que beaucoup d'entre eux ne parviennent pas à le faire lors de la conception des classes.

  2. Jetez un oeil à ce framework Rx quelqu'un a mentionné. Le modèle de thread à utiliser, pour une telle situation que vous avez décrite, n'est pas facilement decidable sans plus de détails au sujet du cas d'utilisation à mon humble avis. Assurez-vous que vous savez ce que vous faites avec les threads. Beaucoup de gens ne peuvent pas comprendre les threads pour sauver leur vie; c'est pas difficile, étant de la bande de roulement coffre-fort peut être lors de la (re)à l'aide de code. Rappelez-vous les contrôleurs doivent souvent être distincts de l'objet du contrôle de gestion (E. g. pas de la même classe); si vous ne le connaissez pas, cherchez un livre sur l'architecture MVC et acheter de la bande des quatre.

  3. Dépend de ce que vous avez besoin. Pour de nombreuses applications, une classe qui est presque entièrement rempli avec des données statiques, est assez bon; comme un singleton pour gratuit. Cela peut être fait très OO. Parfois vous serait plutôt plusieurs instances ou de jouer avec l'injection, ce qui le rend plus complexe.

  4. Je suggère de threads et d'événements. La facilité de rendre le code événementiel est en fait l'une des plus belle choses au sujet de C# à mon humble avis.

Hmm, tuant les singletons...

Dans mon expérience, beaucoup de la plus commune utilise que des jeunes programmeurs de mettre des singletons, ne sont guère plus que des déchets de la classe de mots clés. Savoir quelque chose qu'ils voulaient dire qu'avec un état de module en cours de déploiement dans un highlander classe; et il y a des mauvaises singleton implémentations de là à égaler. Si c'est parce qu'ils n'ont pas réussi à savoir ce qu'ils font, ou seulement avaient Java au collège, je ne sais pas. De retour dans C de la terre, elle est appelée à l'aide de données à la portée du fichier et exposer une API. En C# (et Java) vous êtes en quelque sorte lié à une classe plus que de nombreuses langues. OUPS != classe de mots-clés; apprendre la lhs.

Un décemment écrit la classe peut utiliser des données pour mettre en œuvre efficacement un singleton, et de faire le compilateur ne le travail des jambes de garder un, ou comme l'une que vous allez jamais à obtenir de quoi que ce soit. Ne PAS remplacer les singletons avec l'héritage, à moins que vous sérieusement de savoir ce que le diable que vous faites. Mal fait de l'héritage de telles choses, conduit à de plus en plus fragiles code qui connaît sa réputation pour beaucoup. Les Classes doivent être muet, les données smart. Cela semble stupide, sauf si vous regardez la déclaration profondément. L'utilisation de l'héritage à mon humble avis, pour une telle chose, est généralement une mauvaise chose(tm), les langues, le concept de modules/packages pour une raison.

Si vous êtes pour elle, hé, vous n'avez convertir les singletons il y a des siècles droit? Asseyez-vous et pensez un peu: comment puis-je le mieux la structure de cette application, afin de le faire fonctionner XXX, alors, de penser comment le faire XXX manière impacts de choses, par exemple le fait d'une façon va être une source de conflit entre les threads? Vous pouvez aller sur beaucoup de choses en une heure comme ça. Quand vous vieillissez, vous apprendrez les meilleures techniques.

Voici une suggestion pour un XXX façon de commencer avec: (visualiser) écrire(^Hing) un composite de contrôleur de classe, qui fonctionne comme un gestionnaire sur les objets qu'il référence. Ces objets ont été vos singletons, pas le contrôleur est titulaire d'eux, et qu'ils sont, mais des instances de ces classes. Ce n'est pas le meilleur design pour un grand nombre d'applications (en particulier peut être un problème fortement filetée chers à mon humble avis), mais il ne résout généralement quelles sont les causes de la plupart des jeunes à atteindre pour un singleton, et il va effectuer convenablement pour un large éventail de programmes. C'est euh, comme le modèle de conception CS 102. Oubliez le singleton que vous avez appris dans CS 607.

Que le contrôle de la classe, peut-être "Application" serait plus adéquat ;), essentiellement pour résoudre vos besoin pour les singletons et pour le stockage de la configuration. Comment le faire dans un sublime OO moyen (en supposant que vous ne comprendre la programmation orientée objet) et de ne pas se tirer dans le pied (de nouveau), est un exercice pour votre propre éducation.

Si elle montre, je ne suis pas un fan de ce qui est appelé le pattern singleton, en particulier la façon dont il est souvent utilisé à mauvais escient. Le déplacement d'une base de code loin d'elle, dépend souvent de la façon dont beaucoup de refactoring vous êtes prêt à l'utiliser. Les Singletons sont comme les variables globales: pratique mais pas de beurre. Hmm, je pense que je vais mettre ça dans mes citations de fichier, a une belle phrase...

Honnêtement, vous en savez plus sur le code de base et l'application en question, alors n'importe qui ici. Donc, personne ne peut le concevoir pour vous, et des conseils en parle moins de l'action, à moins d'où je viens.

0voto

Oak Points 10667

J'ai récemment eu à s'attaquer à un problème similaire, et ce que j'ai fait semble bien fonctionner pour moi, peut-être qu'il va vous aider à:

(1) Groupe tous les "global" de l'information en une seule classe. Appelons - Configuration.

(2) Pour toutes les classes qui l'habitude d'utiliser ces objets statiques, les modifier (en fin de compte), héritent d'une nouvelle classe de base abstraite qui ressemble à quelque chose comme

abstract class MyBaseClass {
    protected Configuration config; // You can also wrap it in a property
    public MyBaseClass(Configuration config) {
        this.config = config;
    }
}

(3) Changement de tous les constructeurs de classes dérivant de MyBaseClass en conséquence. Puis il suffit de créer une instance de l' Configuration au début et à la transmission de partout.

Inconvénients:

  • Vous avez besoin de revoir beaucoup de votre constructeur et chaque lieu dans lequel ils sont appelés
  • Cela ne fonctionnera pas bien si vous n'avez pas dériver vos classes de haut niveau de l'Objet. Eh bien, vous pouvez ajouter l' config champ de la classe dérivée, il est un peu moins élégant.

Pros

  • Pas beaucoup d'efforts pour juste modifier l'héritage et constructeurs, et bang - vous pouvez passer tous Configuration.Instance avec config.
  • Vous vous débarrasser de variables statiques complètement; donc, pas de problèmes maintenant, si, par exemple, votre application se transforme soudain en une bibliothèque et que quelqu'un essaie d'invoquer plusieurs méthodes en même temps ou quoi que ce soit.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X