L'utilisation des classes *** Helper ou *** Util contenant uniquement des méthodes statiques est-elle un AntiPattern

19

Je suis souvent confronté à des classes d'aide ou d'utilisation en Java ou dans n'importe quel type de langage. Je me demandais donc s'il s'agit d'une sorte d'Anti Pattern et l'existence de ce type de classes n'est qu'un manque de manque dans la conception et l'architecture d'un logiciel.

Souvent, ces classes sont confinées en utilisant uniquement des méthodes statiques, qui font beaucoup de choses. Mais la plupart du temps, cela dépend du contexte et de l'état.

Ma question est, quelle est votre opinion sur ce type de classes d'aide / utilitaire statiques, car l'avantage est bien sûr l'invocation rapide en utilisant uniquement le nom de la classe.

Et à quel type de niveau d'abstraction éviteriez-vous d'utiliser ce genre de classes?

À mon avis, le mot clé "statique" devrait simplement être autorisé dans la déclaration d'une classe (Java) et non pour les méthodes. À mon avis, en l'utilisant de cette façon, cela pourrait être une bonne alternative et un moyen terme pour pouvoir combiner les paradigmes Procuedural et OO en Java et éviter une mauvaise utilisation du mot-clé.

Ajouts dus aux réponses:

Au début, je pense qu'il est tout à fait légal de pouvoir combiner différents paradigmes et même d'utiliser des langages de script interprétés au cours de l'exécution dans du code compilé machine ou vm.

D'après mon expérience, au cours du processus de développement d'un projet, ce type d'aides et d'utilitaires ou quel que soit le nom, grandit et grandit et est utilisé dans tous les coins oubliés de la base de code, qui était à l'origine conçue pour être modulaire et flexible. Et en raison d'un manque de temps pour effectuer des refactorisations ou repenser la conception, vous ne faites qu'empirer les choses au fil du temps.

Je pense que staticdevrait être supprimé de Java. Surtout maintenant où il est possible d'utiliser des éléments de langage fonctionnel encore plus sophistiqués.

La diversité
la source
Je pense que si l'assistant n'a pas besoin d'instancier une classe, ça va. Le problème est lorsque l'assistant instancie une implémentation concrète d'une classe et que vous n'êtes pas en mesure de vous moquer de cette interface. Si l'aide obtient une interface ou une classe abstraite, je pense que c'est ok.
bobek
C'est bien si vous prétendez faire de la programmation procédurale. Ce n'est pas si vous prétendez faire de la programmation objet (orientée).
Repéré le
1
@Spotted: et comme il n'y a aucune valeur commerciale à prétendre que vous faites uniquement de la programmation orientée objet, faites toujours de la programmation à paradigme mixte et faites de la merde.
RemcoGerlich
@RemcoGerlich Exactement. Mon point était de noter que la réponse est principalement "philosophique", liée à quel paradigme de programmation vous la considérez.
Repéré le

Réponses:

20

Eh bien, Java manque de fonctions libres, vous êtes donc obligé de les mettre en tant que fonctions statiques dans une classe pro-forma. Un contournement nécessaire n'est jamais un anti-modèle, bien qu'il puisse manquer d'élégance.

Ensuite, il n'y a rien de mal avec les fonctions gratuites. En fait, l'utilisation de fonctions gratuites réduit le couplage, car elles n'ont accès qu'à l'interface publique au lieu de tous les détails sanglants.

Bien sûr, l'utilisation de fonctions libres / statiques n'atténue en aucun cas les dangers d'un état partagé mutable, en particulier global.

Déduplicateur
la source
2
@TimothyTruckle Soit ils sont statiques, soit ils ont un superflu this, et nécessitent que quelque chose soit instancié de manière appropriée
Caleth
6
@TimothyTruckle Parce que c'est Java. D'autres langages peuvent avoir des fonctions libres sans avoir à les mettre dans une classe uniquement pour déclarer qu'il ne s'agit pas d'une méthode d'instance. Et à un moment donné, vous devez avoir un couplage serré. Que ce soit bon ou pas est tout à fait à ce que la fonction en question ne .
nvoigt
5
@TimothyTruckle Absolument, le maintien de l'état serait la principale raison d'être "pas bon".
nvoigt
2
@nvoigt, je pense que cette réponse serait grandement améliorée en indiquant explicitement ce point. Les statiques avec état sont mauvaises; les apatrides sont bons. Vraiment assez simple.
David Arno
3
@TimothyTruckle Ce n'est pas un couplage serré. Vous décrivez un couplage lâche. Un couplage étroit dans ce contexte impliquerait une sorte d'interdépendance. Une dépendance à sens unique ne répond pas à cette exigence.
JimmyJames
18

Les fonctions utilitaires ou d'assistance statiques ne sont pas un anti-modèle si elles respectent certaines directives:

  1. Ils devraient être exempts d'effets secondaires

  2. Ils sont utilisés pour un comportement spécifique à l'application qui n'appartient pas à la classe ou aux classes sur lesquelles ces fonctions opèrent

  3. Ils ne nécessitent aucun état partagé

Cas d'utilisation courants:

  • Formatage des dates d'une manière spécifique à l'application
  • Fonctions qui prennent un type en entrée et retournent un type différent.

Par exemple, dans une application sur laquelle j'ai travaillé, les utilisateurs conservent des entrées de journal pour leurs activités. Ils peuvent spécifier une date de suivi et télécharger un rappel d'événement. Nous avons créé une classe utilitaire statique pour prendre une entrée de journal et renvoyer le texte brut d'un fichier .ics.

Cela ne nécessitait aucun état partagé. Il ne modifiait aucun état et la création d'un événement iCal était certainement spécifique à l'application et n'appartenait pas à la classe d'entrée de journal.

Si les fonctions statiques ou les classes utilitaires ont des effets secondaires ou nécessitent un état partagé, je recommanderais de réévaluer ce code, car il introduit un couplage qui peut être difficile à simuler pour les tests unitaires.

Greg Burghardt
la source
4

Cela dépendra de votre approche. De nombreuses applications sont écrites avec un état d'esprit plus fonctionnel, contrairement à l'état d'esprit classique (y compris une grande partie de la suite de sites sur laquelle vous vous trouvez).

Avec cet état d'esprit, il y aura de nombreuses méthodes utilitaires sur les classes de travail qui peuvent être regroupées en tant que méthodes statiques. Ils sont nourris / utilisés plus près d'objets simples qui ne contiennent que des données et sont transmis.

C'est une approche valable et peut très bien fonctionner, surtout à grande échelle.

Tracker1
la source
"peut très bien fonctionner, en particulier à grande échelle". Non. Le couplage étroit causé par l'accès statique se dressera bientôt sur votre chemin à mesure que le projet se développe.
Timothy Truckle
@TimothyTruckle Pourriez-vous expliquer comment Agent.Save (obj) est tellement plus étroitement couplé qu'obj.Save ()? L'agent est tout aussi capable d'obtenir une référence DI / IoC, même pour les méthodes statiques qu'une instance de classe. Pour référence, Stack Exchange lui-même est écrit dans ce type de mentalité et a évolué mieux que toute autre application ASP.Net avec moins de ressources que toutes les autres que je connaisse.
Tracker1
"Pourriez-vous expliquer comment Agent.Save (obj) est tellement plus étroitement couplé qu'obj.Save ()?" Ce n'est pas le bon exemple. Un exemple serait correct par Agent.Save(obj)rapport agentInstance.Save(obj). Avec ce dernier, vous avez la possibilité d' injecter agentInstance dans le code utilisateur. Par conséquent , vous pouvez injecter une classe enfant de Agenttrop qui permet la réutilisation Og code à l' aide de différents « types » de Agentsans recompilation. Il s'agit d'un faible couplage .
Timothy Truckle
Je pense que cela se résume vraiment à deux ou trois choses. Un état est-il nécessaire, dans quelle mesure doit-il être flexible et est-il acceptable d'avoir un état global / instance? C'est à chaque développeur / architecte de décider. Je préférerais une base de code plus simple que d'ajouter de la complexité pour des scénarios qui rendent tout plus difficile à comprendre dans son ensemble. L'une des choses que j'aime chez node / js est que je peux écrire du code simple / propre et toujours injecter pour le test sans le code écrit sur les spécificités DI / IoC.
Tracker1
2

Personnellement, je pense que la seule partie importante de ces classes d'assistance est qu'elles deviennent privées. En plus de cela - ils sont strictement une bonne idée (appliqués avec parcimonie).

La façon dont je pense à cela - c'est si dans l'implémentation d'une classe (ou d'une fonction) - quelque chose comme ça est utile, et rend cette implémentation plus claire, comment cela pourrait-il être mauvais? Et souvent, il est essentiel de définir de telles classes d'assistance privées pour permettre l'intégration et l'utilisation d'autres algorithmes qui dépendent de la forme particulière des données.

Que ce soit pour l'étiqueter «aide» est une petite question de goût personnel, mais cela implique qu'il aide à la mise en œuvre et qu'il ne présente aucun intérêt / utilisation pour un public plus large. Si c'est ce qui a du sens - allez-y!

Lewis Pringle
la source
1
Les classes d'utilité publique peuvent toujours être utiles. Premier exemple: java.lang.Math.
amon
1

La prévalence des staticclasses auxiliaires est basée sur une idée fausse. Ce staticn'est pas parce que nous appelons des classes avec uniquement des méthodes "classes utilitaires" qu'il n'est pas autorisé d'écrire un comportement courant dans les POJO.

static les classes d'assistance sont anti-motif pour trois raisons:

  1. L'accès statique à ces méthodes d'assistance masque les dépendances . Si ces "classes d'utilité" étaient des POJO, vous pourriez les injecter dans une classe dépendante en tant que paramètres de constructeur, ce qui rendrait la dépendance évidente pour tout utilisateur d'une classe dépendante.

  2. L'accès statique à ces méthodes d'assistance entraîne un couplage serré . Cela signifie que le code utilisant les méthodes d'assistance est difficile à réutiliser et (comme effet secondaire) difficile à tester.

  3. Surtout s'ils maintiennent l' état, ce ne sont que des variables globales . Et j'espère que personne ne prétend que les variables globales sont bonnes ...

Les classes d'assistance statiques font partie du modèle anti code STUPID .


L'état global n'est pas lié à la question, - max630

L'OP a écrit:

Mais la plupart du temps, cela dépend du contexte et de l'état.

Les constructions statiques statiques sont des états globaux.

tout type de code peut l'utiliser. - max630

Oui, de cause. Et presque toutes les applications ont besoin d'une sorte d' état global .

Mais état global ! = Variable globale .

Vous pouvez créer un état global avec des techniques OO via l' injection de dépendances. Mais vous ne pouvez pas éviter un état global avec des structures statiques statiques qui sont des variables globales en bas.

Timothy Truckle
la source
2
L'état global n'est pas lié à la question, tout type de code peut l'utiliser.
max630
@ max630 s'il vous plaît voir ma mise à jour
Timothy Truckle
1
Vous pouvez utiliser des fonctions sans statique sans couplage. Vous contournez des Func<Result, Args>objets qui sont instanciés ailleurs.
Caleth
1
1) Je pense généralement que masquer la mise en œuvre est une bonne chose, lorsqu'elle convient. 2) Le fait d'avoir tout comme arguments / injection de dépendance crée également une sorte de couplage au moment du design, vous pouvez le ressentir lorsque vous devez consacrer beaucoup de temps à changer les signatures dans de longues chaînes de dépendance. Et comme avoir chaque fonction nécessitant votre enregistreur, ou d'autres préoccupations transversales, comme argument, urgh ... (Mais oui, cela peut rendre les tests de bas niveau plus difficiles) 3) Bien sûr, les fonctions statiques doivent être sans état.
Alex
1
@Alex Alors laissez-moi le dire dans l'autre sens: Une unité est un tas de code (y compris les méthodes "utilitaires" référencées statiques) qui a la même raison de changer . Tout ce qui se trouve à l'intérieur de cette unité est un détail d'implémentation . Tout le reste est une dépendance et l'unité ne doit pas y avoir d'accès statique.
Timothy Truckle