Je crée parfois des classes «Util» qui servent principalement à contenir des méthodes et des valeurs qui ne semblent pas vraiment appartenir ailleurs. Mais chaque fois que je crée une de ces classes, je pense "euh-oh, je vais le regretter plus tard ...", parce que j'ai lu quelque part que c'était mauvais.
Mais d'un autre côté, il semble y avoir deux cas convaincants (du moins pour moi) pour eux:
- secrets d'implémentation utilisés dans plusieurs classes d'un package
- fournissant des fonctionnalités utiles pour augmenter une classe, sans encombrer son interface
Suis-je sur le chemin de la destruction? Ce que tu dis !! Dois-je refactoriser?
Util
dans les noms de vos classes. Problème résolu.SomethingUtil
est un peu paresseux et obscurcit simplement le véritable objectif de la classe - même avec les classes nomméesSomethingManager
ouSomethingService
. Si cette classe a une seule responsabilité, il devrait être facile de lui donner un nom significatif. Sinon, c'est le vrai problème à résoudre ...Util
, bien que je ne m'attendais évidemment pas à ce que cela soit résolu et que le reste de la question soit ignoré ...Réponses:
Le design OO moderne accepte que tout n'est pas un objet. Certaines choses sont des comportements ou des formules, et certaines n'ont pas d'état. Il est bon de modéliser ces choses en tant que fonctions pures pour bénéficier de cette conception.
Java et C # (et autres) vous obligent à créer une classe util et à parcourir ce cerceau pour le faire. Ennuyeux, mais pas la fin du monde; et pas vraiment gênant du point de vue du design.
la source
Ne jamais dire jamais"
Je ne pense pas que ce soit nécessairement mauvais, c'est seulement mauvais si vous le faites mal et en abusez.
Nous avons tous besoin d'outils et d'utilitaires
Pour commencer, nous utilisons tous des bibliothèques qui sont parfois considérées comme presque omniprésentes et incontournables. Par exemple, dans le monde Java, Google Guava ou certains d' Apache Commons ( Apache Commons Lang , Apache Commons Collections , etc ...).
Il y a donc clairement un besoin pour cela.
Évitez les mots durs, la duplication et l'introduction de bogues
Si vous pensez à ce sont à peu près juste un groupe très grand de ces
Util
classes que vous décrivez, sauf que quelqu'un est passé par beaucoup de mal pour les obtenir (relativement) à droite, et ils ont été le temps - testé et fortement balled les yeux par d' autres.Donc, je dirais que la première règle générale lorsque vous ressentez la démangeaison d'écrire une
Util
classe est de vérifier que laUtil
classe n'existe pas déjà.Le seul contre-argument que j'ai vu pour cela est quand vous voulez limiter vos dépendances parce que:
Mais ces deux éléments peuvent être résolus en reconditionnant la bibliothèque à l' aide de ProGuard ou un équivalent, ou en le démontant vous-même (pour les utilisateurs de Maven , le plug-in maven - shadow offre certains modèles de filtrage pour l'intégrer dans votre build).
Donc, s'il se trouve dans une bibliothèque et correspond à votre cas d'utilisation, et qu'aucun benchmark ne vous dit le contraire, utilisez-le. Si cela diffère un peu de ce que vous, étendez-le (si possible) ou étendez-le, ou en dernier recours réécrivez-le.
Conventions de nommage
Cependant, jusqu'à présent dans cette réponse, je les ai appelés
Util
comme vous. Ne les nommez pas ainsi.Donnez-leur des noms significatifs. Prenez Google Guava comme (très, très) bon exemple de ce qu'il faut faire, et imaginez simplement que l'
com.google.guava
espace de noms est en fait votreutil
racine.Appelez votre forfait
util
, au pire, mais pas les cours. S'il s'agit d'String
objets et de manipulation de constructions de chaînes, appelezStrings
-le nonStringUtils
(désolé, Apache Commons Lang - je vous aime et vous utilise toujours!). S'il fait quelque chose de spécifique, choisissez un nom de classe spécifique (commeSplitter
ouJoiner
).Test de l'unité
Si vous devez recourir à l'écriture de ces utilitaires, assurez-vous de les tester à l'unité. La bonne chose à propos des utilitaires est qu'ils sont généralement des composants plutôt autonomes, qui acceptent des entrées spécifiques et renvoient des sorties spécifiques. Voilà le concept. Il n'y a donc aucune excuse pour ne pas les tester à l'unité.
De plus, les tests unitaires vous permettront de définir et de documenter le contrat de leur API. Si les tests échouent, soit vous avez changé quelque chose dans le mauvais sens, soit cela signifie que vous essayez de changer le contrat de votre API (ou que vos tests originaux étaient de la merde - apprenez-en et ne recommencez pas) .
Conception d'API
La décision de conception que vous prendrez pour ces API vous suivra peut-être longtemps. Donc, tout en ne passant pas des heures à écrire un
Splitter
-clone, faites attention à la façon dont vous abordez le problème.Posez-vous quelques questions:
Vous voulez que ces utilitaires couvrent un large éventail de cas d'utilisation, qu'ils soient robustes, stables, bien documentés, selon le principe de la moindre surprise, et qu'ils soient autonomes. Idéalement, chaque sous-ensemble de vos utilitaires, ou votre ensemble complet d'utilisation au moins, doit être exportable en un ensemble pour une réutilisation facile.
Comme d'habitude, apprenez des géants ici:
util
package Java (qui contient la bibliothèque Collections) et son équivalent .Net ,Oui, beaucoup d'entre eux mettent l'accent sur les collections et les structures de données, mais ne me dites pas que ce n'est pas où ni pour quoi vous êtes généralement susceptible d'implémenter la plupart de vos utilitaires, directement ou indirectement.
la source
If deals with String objects and manipulation of string constructs, call it Strings, not StringUtils
StringsCollectionTypeHere
si vous voulez une implémentation concrète. Ou un nom plus spécifique si ces chaînes ont une signification spécifique dans le contexte de votre application. Pour ce cas particulier, Guava utiliseStrings
pour ses aides liées aux chaînes, par opposition àStringUtils
Commons Lang. Je le trouve parfaitement acceptable, cela signifie simplement pour moi que la classe gère l'String
objet ou est une classe à usage général pour gérer les chaînes.NameUtils
choses me bousculent sans fin parce que si elle se trouve sous un nom de package clairement étiqueté, je saurais déjà que c'est une classe utilitaire (et sinon, je le découvrirais rapidement en regardant l'API). Il est aussi gênant pour moi que les gens déclarant des choses commeSomethingInterface
,ISomething
ouSomethingImpl
. J'étais en quelque sorte OK avec de tels artefacts lors du codage en C et sans utiliser d'IDE. Aujourd'hui, je n'aurais généralement pas besoin de telles choses.Les classes util ne sont pas cohérentes et sont généralement de mauvaise conception car une classe doit avoir une seule raison de changer (principe de responsabilité unique).
Et pourtant, je l' ai vu « util » classes dans la très Java API, comme:
Math
,Collections
etArrays
.Ce sont, en fait, des classes utilitaires mais toutes leurs méthodes sont liées à un seul thème, l'une a des opérations mathématiques, une a des méthodes pour manipuler les collections et l'autre pour manipuler les tableaux.
Essayez de ne pas avoir de méthodes totalement indépendantes dans une classe utilitaire. Si tel est le cas, il y a de fortes chances que vous puissiez aussi les placer ailleurs où ils appartiennent vraiment.
Si doit avoir util classes , puis les essayer d'être séparés par thème comme de Java
Math
,Collections
etArrays
. Cela, au moins, montre une certaine intention de conception même quand ce ne sont que des espaces de noms.Pour ma part, évitez toujours les classes utilitaires et n'avez jamais eu besoin d'en créer une.
la source
Math
ouArrays
montre au moins une intention de conception.Il est parfaitement acceptable d'avoir util cours, bien que je préfère le terme
ClassNameHelper
. Le .NET BCL contient même des classes d'assistance. La chose la plus importante à retenir est de bien documenter le but des classes, ainsi que chaque méthode d'aide individuelle, et de lui donner un code maintenable de haute qualité.Et ne vous laissez pas emporter par les cours d'aide.
la source
J'utilise une approche à deux niveaux. Une classe "Globals" dans un package "util" (dossier). Pour que quelque chose entre dans une classe "Globals" ou un package "util", il doit être:
Exemples qui réussissent ces tests:
Voici un exemple d'une très petite méthode d'assistance, complètement indépendante du reste de l'application:
En regardant cela, je peux voir un bug qui 21 devrait être "21e", 22 devrait être "22e", etc., mais ce n'est pas la question.
Si l'une de ces méthodes d'assistance se développe ou se complique, elle doit être déplacée dans sa propre classe dans le package util. Si deux ou plusieurs classes d'assistance dans le package util sont liées entre elles, elles doivent être déplacées dans leur propre package. Si une méthode constante ou auxiliaire s'avère être liée à une partie spécifique de votre application, elle doit y être déplacée.
Vous devriez être en mesure de justifier pourquoi il n'y a pas de meilleur endroit pour tout ce que vous collez dans une classe Globals ou un package d'utilisation et vous devez le nettoyer régulièrement en utilisant les tests ci-dessus. Sinon, vous créez juste un gâchis.
la source