web-dev-qa-db-fra.com

Modèle C # pour gérer proprement les "fonctions libres", en évitant les classes statiques de "sac utilitaire" de style assistant

Je passais récemment en revue quelques classes statiques de "sac utilitaire" de style Helper flottant autour de grandes bases de code C # avec lesquelles je travaille, des choses comme l'extrait de code très condensé suivant:

// Helpers.cs

public static class Helpers
{
    public static void DoSomething() {}
    public static void DoSomethingElse() {}
}

Les méthodes spécifiques que j'ai examinées sont

  • la plupart du temps sans rapport les uns avec les autres,
  • sans état explicite a persisté à travers les invocations,
  • petit, et
  • chacun consommé par une variété de types non liés.

Modifier: ce qui précède n'est pas censé être une liste de problèmes allégués. C'est une liste de caractéristiques communes des méthodes spécifiques que j'examine. C'est un contexte pour aider les réponses à fournir des solutions plus pertinentes.

Juste pour cette question, je ferai référence à ce type de méthode comme un GLUM (méthode générale de l'utilitaire léger). La connotation négative de "morosité" est en partie voulue. Je suis désolé si cela apparaît comme un jeu de mots stupide.

Même en mettant de côté mon propre scepticisme par défaut à l'égard des GLUM, je n'aime pas les choses suivantes à ce sujet:

  • Une classe statique est utilisée uniquement comme espace de noms.
  • L'identifiant de classe statique n'a pratiquement aucun sens.
  • Lorsqu'un nouveau GLUM est ajouté, soit (a) cette classe "bag" est touchée sans raison valable, soit (b) une nouvelle classe "bag" est créée (ce qui en soi n'est généralement pas un problème; ce qui est mauvais, c'est que le nouveau les classes statiques ne font souvent que répéter le problème de non-relation, mais avec moins de méthodes).
  • La méta-dénomination est inévitablement horrible, non standard et généralement incohérente en interne, que ce soit Helpers, Utilities, ou autre.

Quel est un modèle raisonnablement bon et simple pour refactoriser cela, de préférence en répondant aux préoccupations ci-dessus, et de préférence avec un toucher aussi léger que possible?

Je devrais probablement souligner: Toutes les méthodes avec lesquelles j'ai affaire ne sont pas liées les unes aux autres. Il ne semble pas y avoir de moyen raisonnable de les décomposer en plus fins mais toujours multi-membres sacs de méthode de classe statique.

9
William

Adoptez simplement la convention selon laquelle les classes statiques sont utilisées comme des espaces de noms dans des situations comme celle-ci en C #. Les espaces de noms obtiennent de bons noms, tout comme ces classes. Le using static la fonctionnalité de C # 6 facilite également la vie.

Les noms de classe malodorants comme Helpers signalent que les méthodes doivent être divisées en classes statiques mieux nommées, si possible, mais l'une de vos hypothèses soulignées est que les méthodes avec lesquelles vous avez affaire sont "non liées par paire" , ce qui implique la division en une nouvelle classe statique par méthode existante. C'est probablement bien, si

  • il y a de bons noms pour les nouvelles classes statiques et
  • vous le jugez bénéfique pour la maintenabilité de votre projet.

À titre d'exemple artificiel, Helpers.LoadImage pourrait devenir quelque chose comme FileSystemInteractions.LoadImage.

Pourtant, vous pourriez vous retrouver avec des sacs de méthode de classe statique. Voici comment cela peut se produire:

  • Peut-être que seulement certaines (ou aucune) des méthodes originales ont de bons noms pour leurs nouvelles classes.
  • Peut-être que les nouvelles classes évolueront plus tard pour inclure d'autres méthodes pertinentes.
  • Peut-être que le nom de la classe d'origine ne sentait pas mauvais et était en fait correct.

Il est important de se rappeler que ces sacs de méthode de classe statique ne sont pas très rares dans les vraies bases de code C #. Ce n'est probablement pas pathologique d'en avoir quelques petits .


Si vous voyez vraiment un avantage à avoir chaque méthode de type "fonction libre" dans son propre fichier (ce qui est bien et utile si vous jugez honnêtement que cela profite réellement à la maintenabilité de votre projet), vous pourriez envisager de faire une telle classe statique au lieu d'une statique classe partielle, en utilisant le nom de classe statique de la même manière que vous le feriez pour un espace de noms, puis en le consommant via using static. Par exemple:

structure of dummy project using this pattern

Program.cs

namespace ConsoleApp1
{
    using static Foo;
    using static Flob;

    class Program
    {
        static void Main(string[] args)
        {
            Bar();
            Baz();
            Wibble();
            Wobble();
        }
    }
}

Foo/Bar.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Bar() { }
    }
}

Foo/Baz.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Baz() { }
    }
}

Flob/Wibble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wibble() { }
    }
}

Flob/Wobble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wobble() { }
    }
}
5
William

Je pense que vous avez deux problèmes étroitement liés.

  • Les classes statiques contiennent des fonctions logiquement indépendantes
  • Les noms des classes statiques ne fournissent pas d'informations utiles sur la signification/le contexte des fonctions contenues.

Ces problèmes peuvent être résolus en combinant uniquement des méthodes logiquement liées dans une classe statique et en déplaçant les autres dans leur propre classe statique. En fonction de votre domaine problématique, vous et votre équipe devez décider des critères que vous utiliserez pour séparer les méthodes en classes statiques associées.

Préoccupations et solutions possibles

Les méthodes sont pour la plupart indépendantes les unes des autres - problème. Combinez les méthodes liées sous une classe statique et déplacez les méthodes non liées vers leur propre statique Des classes.

Les méthodes sans état explicite sont persistées à travers les invocations - pas un problème. Les méthodes sans état sont une fonctionnalité, pas un bogue. L'état statique est difficile à tester de toute façon et ne doit être utilisé que si vous devez conserver l'état à travers les appels de méthode, mais il existe de meilleures façons de le faire, telles que les machines d'état et le mot clé yield.

Les méthodes sont petites - pas un problème. - Les méthodes devraient être petit.

Les méthodes sont chacune consommées par une variété de types non liés - pas un problème. Vous avez créé une méthode générale qui peut être re -utilisé dans de nombreux endroits non liés.

Une classe statique est utilisée uniquement comme un espace de noms - pas un problème. C'est ainsi que les méthodes statiques sont utilisées. Si ce style d'appels de méthodes vous dérange, essayez d'utiliser des méthodes d'extension ou le using static déclaration.

L'identifiant de classe statique n'a pratiquement aucun sens - problem. Cela n'a pas de sens car les développeurs y mettent des méthodes indépendantes. Mettez des méthodes non liées dans leurs propres classes statiques et donnez-leur des noms spécifiques et compréhensibles.

Lorsqu'un nouveau GLUM est ajouté, soit (a) cette classe "bag" est touchée (tristesse SRP) - pas de problème = si vous suivez l'idée que seules les méthodes logiquement liées doivent être dans une seule classe statique. SRP n'est pas violé ici, car le principe doit être appliqué aux classes ou aux méthodes. La responsabilité unique des classes statiques signifie contenir différentes méthodes pour une "idée" générale. Cette idée peut être une fonctionnalité ou une conversion, ou des itérations (LINQ), ou la normalisation des données, ou la validation ...

ou moins fréquemment (b) une nouvelle classe "bag" est créée (plus de sacs) - pas de problème. Classes/classes/fonctions statiques - sont nos outils (développeurs) que nous utilisons pour concevoir des logiciels. Votre équipe a-t-elle des limites d'utilisation des cours? Quelles sont les limites maximales pour "plus de sacs"? La programmation est une question de contexte, si pour trouver une solution dans votre contexte particulier, vous vous retrouvez avec 200 classes statiques qui sont naturellement nommées, placées logiquement dans des espaces de noms/dossiers avec une hiérarchie logique - ce n'est pas un problème.

La méta-dénomination est inévitablement horrible, non standard et généralement incohérente en interne, que ce soit les Helpers, les utilitaires ou autre - problème. Fournissez de meilleurs noms qui décrivent le comportement attendu. Conservez uniquement les méthodes associées dans une classe, ce qui vous aide à mieux nommer.

17
Fabio