¿Es el uso de *** Helper o *** Util clases que contienen solo métodos estáticos un AntiPattern

19

A menudo me enfrento con clases auxiliares o de utilidad en Java o cualquier tipo de lenguaje. Entonces me preguntaba si esto es algún tipo de Anti Pattern y la existencia de este tipo de clases es solo una falta de fallas en el diseño y la arquitectura de un Software.

A menudo, estas clases se limitan utilizando solo métodos estáticos, que hacen muchas cosas. Pero sobre todo depende del contexto y tiene estado.

Mi pregunta es, ¿cuál es su opinión sobre este tipo de clases auxiliares / util estáticas porque la ventaja es, por supuesto, la invocación rápida usando solo el nombre de la clase?

¿Y a qué tipo de abstracción evitarías usar este tipo de clases?

En mi opinión, la palabra clave "estática" debería permitirse dentro de la declaración de una clase (Java) y no para los métodos. En mi opinión, usarlo de esta manera podría ser una buena alternativa y un medio para poder combinar Procuedural- y OO-Paradigms en Java y evitar el mal uso de la palabra clave.

Adiciones debido a las respuestas:

Al principio, creo que es completamente legal poder combinar diferentes paradigmas e incluso usar lenguajes de script interpretados en tiempo de ejecución dentro de código compilado de máquina o vm.

Mi experiencia es que durante el proceso de desarrollo de un proyecto, este tipo de ayudantes y utilidades, o como se llame, crecen y crecen y se usan en cada rincón olvidado de la base de código, que originalmente fue diseñada para ser modular y flexible. Y debido a la falta de tiempo para hacer refactorizaciones o pensar en el diseño nuevamente, solo lo empeora mucho con el tiempo.

Creo que staticdebería eliminarse de Java. Especialmente ahora donde es posible usar elementos de lenguaje funcional aún más sofisticados.

Diversidad
fuente
Creo que si el ayudante no necesita crear una instancia de una clase, está bien. El problema es cuando el ayudante crea una implementación concreta de una clase y luego no puede burlarse de esa interfaz. Si el ayudante pasa una interfaz o una clase abstracta, creo que está bien.
bobek
Está bien si afirmas que haces programación de procedimientos. No lo es si afirmas que haces programación de objetos (orientada).
Visto el
1
@Spotted: y como no tiene ningún valor comercial afirmar que solo hace programación orientada a objetos, simplemente haga una programación de paradigma mixto y haga las cosas bien.
RemcoGerlich
@RemcoGerlich Exactamente. Mi punto fue señalar que la respuesta es principalmente "filosófica", relacionada desde qué paradigma de programación la consideras.
Visto el

Respuestas:

20

Bueno, Java carece de funciones gratuitas, por lo que se ve obligado a colocarlas como funciones estáticas en alguna clase pro forma. Una solución necesaria nunca es un antipatrón, aunque puede carecer de elegancia.

A continuación, no hay nada de malo con las funciones gratuitas. En realidad, el uso de funciones gratuitas reduce el acoplamiento, ya que solo tienen acceso a la interfaz pública en lugar de todos los detalles sangrientos.

Por supuesto, el uso de funciones libres / estáticas no alivia de ninguna manera los peligros del estado compartido mutable, especialmente global.

Deduplicador
fuente
2
@TimothyTruckle O son estáticos, o tienen un exceso this, y requieren que algo sea instanciado adecuadamente
Caleth
66
@TimothyTruckle Porque es Java. Otros lenguajes pueden tener funciones libres sin la sobrecarga de tener que ponerlas en una clase solo para declarar que no es un método de instancia. Y en algún momento, debes tener un acoplamiento apretado. Si eso es bueno o no es totalmente de lo que la función en cuestión hace .
nvoigt
55
@TimothyTruckle Absolutamente, mantener el estado sería la razón principal para ser "no bueno".
nvoigt
2
@nvoigt, creo que esta respuesta mejoraría enormemente al indicar explícitamente ese punto. Las estadísticas estáticas son malas; los apátridas son buenos. Realmente bastante simple.
David Arno
3
@TimothyTruckle Eso no es un acoplamiento apretado. Estás describiendo acoplamiento flojo. Un acoplamiento estricto en este contexto implicaría algún tipo de interdependencia. Una dependencia unidireccional no cumple con ese requisito.
JimmyJames
18

La utilidad estática o las funciones auxiliares no son un antipatrón si cumplen con algunas pautas:

  1. Deben estar libres de efectos secundarios.

  2. Se utilizan para el comportamiento específico de la aplicación que no pertenece a la clase o clases en las que operan estas funciones

  3. No requieren ningún estado compartido

Casos de uso común:

  • Formateo de fechas en una aplicación específica
  • Funciones que toman un tipo como entrada y devuelven un tipo diferente.

Por ejemplo, en una aplicación en la que trabajé, los usuarios mantienen entradas de diario para sus actividades. Pueden especificar una fecha de seguimiento y descargar un recordatorio de evento. Creamos una clase de utilidad estática para tomar una entrada de diario y devolver el texto sin formato para un archivo .ics.

No requería ningún estado compartido. No mutó ningún estado, y crear un evento iCal ciertamente fue específico de la aplicación y no pertenecía a la clase de entrada de diario.

Si las funciones estáticas o las clases de utilidad tienen efectos secundarios o requieren un estado compartido, recomendaría reevaluar este código, ya que introduce un acoplamiento que puede ser difícil de burlar para las pruebas unitarias.

Greg Burghardt
fuente
4

Dependerá de su enfoque. Muchas aplicaciones se escriben con una mentalidad más funcional, en oposición a la clásica (incluyendo gran parte del conjunto de sitios en los que se encuentra).

Con esta mentalidad, habrá muchos métodos de utilidad en las clases de trabajo que se pueden agrupar como métodos estáticos. Se alimentan / usan más cerca de objetos simples que solo contienen datos y se transmiten.

Es un enfoque válido y puede funcionar muy bien, especialmente a escala.

Rastreador1
fuente
"puede funcionar muy bien, especialmente a escala" No. El acoplamiento estrecho causado por el acceso estático pronto se interpondrá en su camino a medida que el proyecto crezca.
Timothy Truckle
@TimothyTruckle ¿Podría explicar cómo Agent.Save (obj) está mucho más acoplado que obj.Save ()? El agente es tan capaz de obtener una referencia DI / IoC, incluso para métodos estáticos como una instancia de clase. Como referencia, Stack Exchange está escrito en este tipo de mentalidad, y se ha ampliado mejor que cualquier otra aplicación ASP.Net con menos recursos que cualquier otra que conozco.
Rastreador1
"¿Podría explicar cómo Agent.Save (obj) está mucho más acoplado que obj.Save ()?" Este es el ejemplo equivocado. Un ejemplo correcto sería Agent.Save(obj)vs agentInstance.Save(obj). Con este último tienes la posibilidad de inyectar agentInstance en el código de uso. En consecuencia, puede inyectar cualquier clase secundaria de Agentlo que permite la reutilización del código de uso con diferentes "tipos" de Agentsin recompilar. Esto es de bajo acoplamiento .
Timothy Truckle
Creo que realmente se reduce a un par de cosas. ¿Se necesita un estado, qué tan flexible debe ser, y está bien tener un estado global / instancia? Eso depende de cada desarrollador / arquitecto para decidir. Prefiero una base de código más simple que agregar complejidad para escenarios que hacen que todo sea más difícil de entender en su conjunto. Una de las cosas que me encantan de node / js es que puedo escribir código simple / limpio y aún inyectar para probar sin el código escrito en los detalles específicos de DI / IoC.
Rastreador1
2

Personalmente, creo que la única parte importante de estas clases de ayuda es que se hagan privadas. Además de eso, son estrictamente una buena idea (aplicada con moderación).

La forma en que pienso sobre esto, es si al implementar una clase (o función), algo como esto es útil y aclara esa implementación, ¿cómo podría ser malo? Y A MENUDO es crítico definir tales clases de ayuda privadas para permitir la integración y el uso de otros algoritmos que dependen de que los datos estén en una forma particular.

Si etiquetarlo como 'ayudante' es una pequeña cuestión de gusto personal, pero connota que ayuda en la implementación y no es de interés / uso para un público más amplio. Si eso es lo que tiene sentido, ¡adelante!

Lewis Pringle
fuente
1
Las clases de utilidad pública aún podrían ser útiles. Primer ejemplo: java.lang.Math.
amon
1

La prevalencia de las staticclases auxiliares se basa en un concepto erróneo. El hecho de que llamemos a las clases con solo staticmétodos "clases de utilidad" no significa que no esté permitido escribir comportamientos comunes en POJO.

static Las clases auxiliares son antipatrón por tres razones:

  1. El acceso estático a estos métodos auxiliares oculta las dependencias . Si estas "clases de utilidad" fueran POJOs, podría inyectarlas int a una clase dependiente como parámetros de construcción, lo que haría obvia la dependencia para cualquier usuario de una clase dependiente.

  2. El acceso estático a estos métodos auxiliares provoca un acoplamiento estrecho . Esto significa que el código que usa los métodos auxiliares es difícil de reutilizar y (como efecto secundario) difícil de probar.

  3. Especialmente si mantienen el estado, estas son meramente variables globales . Y con suerte nadie argumenta que las variables globales son buenas ...

Las clases auxiliares estáticas son parte del patrón anti código STUPID .


El estado global no está relacionado con la pregunta, - max630

El OP escribió:

Pero sobre todo depende del contexto y tiene estado.

Las construcciones con estado estático son estados globales.

Cualquier tipo de código puede usarlo. - max630

Si de causa. Y casi todas las aplicaciones necesitan algún tipo de estado global .

¡Pero estado global ! = Variable global .

Puede crear un estado global con técnicas OO mediante la inyección de dependencia. Pero no puede evitar el estado global con estructuras estáticas con estado que son variables globales en la parte inferior.

Timothy Truckle
fuente
2
El estado global no está relacionado con la pregunta, cualquier tipo de código puede usarlo.
max630
@ max630 por favor vea mi actualización
Timothy Truckle
1
Puede usar funciones libres de estática sin acoplamiento. Usted pasa alrededor de los Func<Result, Args>objetos, que se instancian en otros lugares.
Caleth
1
1) En general, creo que ocultar la implementación es algo bueno, donde sea adecuado. 2) Tener todo como argumentos / dependencia inyectada también crea una especie de acoplamiento en tiempo de diseño, puede sentir esto cuando se ve obligado a pasar mucho tiempo cambiando firmas en largas cadenas de dependencia. Y como tener todas las funciones que requieren su Logger, u otras preocupaciones transversales, como argumento, urgh ... (Pero sí, puede hacer que las pruebas de bajo nivel sean más difíciles) 3) Por supuesto, las funciones estáticas deben ser apátridas.
Alex
1
@Alex Entonces déjenme decirlo al revés: Una unidad es cualquier grupo de código (incluidos los métodos de "utilidad" referenciados estáticos) que tiene la misma razón para cambiar . Cualquier cosa dentro de esta unidad es detalle de implementación . Cualquier otra cosa es una dependencia , y la unidad no debe tener acceso estático a ella.
Timothy Truckle