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 persistait à travers les invocations,
- petit, et
- chacun consommé par une variété de types non liés.
Edit: Ce qui précède n'est pas destiné à être une liste de problèmes allégués. Il s'agit d'une liste des caractéristiques communes des méthodes spécifiques que j'examine. C'est le 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 que répéter le problème de l'absence de lien, mais avec moins de méthodes).
- La méta-nommage est inéluctablement terrible, non standard, et généralement dépourvue de cohérence interne, que ce soit
Helpers
,Utilities
ou autre chose.
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 insister sur le fait que toutes les méthodes auxquelles je fais face ne sont pas liées les unes aux autres. Il ne semble pas y avoir de moyen raisonnable de les décomposer en sacs de méthode de classe statique plus fins mais toujours multi-membres.
la source
Réponses:
Je pense que vous avez deux problèmes étroitement liés.
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 ne sont pour la plupart pas liées les unes aux autres - problème. Combinez les méthodes liées sous une classe statique et déplacez les méthodes non liées vers leurs propres classes statiques.
Les méthodes sont sans état explicite persistées à travers les invocations - ce n'est 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 maintenir 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
yield
mot clé.Les méthodes sont petites - pas un problème. - Les méthodes doivent être petites.
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 réutilisée 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 la
using static
déclaration.L'identifiant de classe statique n'a pratiquement aucun sens - problème . 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 un problème si vous suivez l'idée que seules les méthodes logiquement liées devraient être dans une 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 "sac" est créée (plus de sacs) - pas de problème. Classes / classes statiques / fonctions - 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 nommées de manière compréhensible, logiquement placées dans des espaces de noms / dossiers avec une hiérarchie logique - ce n'est pas un problème.
Le méta-nommage est inévitablement horrible, non standard et généralement incohérent 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.
la source
A
de noms avec 100 classes statiques à méthode unique (dans 100 fichiers) qu'une classe statiqueA
avec 100 méthodes dans un seul fichier.Math
classe statique.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. La
using static
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 paires", ce qui implique une répartition en une nouvelle classe statique par méthode existante. C'est probablement bien, siÀ titre d'exemple artificiel,
Helpers.LoadImage
pourrait devenir quelque chose commeFileSystemInteractions.LoadImage
.Pourtant, vous pourriez vous retrouver avec des sacs de méthode de classe statique. Voici comment cela peut se produire:
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 semblable à une "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 comme vous le feriez pour un espace de noms, puis en le consommant via
using static
. Par exemple:Program.cs
Foo / Bar.cs
Foo / Baz.cs
Flob / Wibble.cs
Flob / Wobble.cs
la source
En règle générale, vous ne devriez pas avoir ou avoir besoin de "méthodes d'utilité générale". Dans votre logique métier. Jetez un autre coup d'œil et placez-les dans un endroit approprié.
Si vous écrivez une bibliothèque de mathématiques ou quelque chose comme ça, je suggère, bien que je les déteste normalement, des méthodes d'extension.
Vous pouvez utiliser des méthodes d'extension pour ajouter des fonctions aux types de données standard.
Par exemple, disons que j'ai une fonction générale MySort () au lieu de Helpers.MySort ou en ajoutant un nouveau ListOfMyThings.MySort je peux l'ajouter à IEnumerable
la source