Funciones estáticas vs clases

8

Digamos que quiero construir algunas funciones de utilidad para hacer algunas matemáticas básicas con la BigDecimals, por ejemplo, quiero tener una función que calcule el promedio de a List<BigDecimal>.

¿Cuál es el mejor enfoque? ¿Una función estática o una clase de utilidad?

public static BigDecimal computeAverage(List<BigDecimal> numbers)

o

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)
S4M
fuente
Un punto a considerar es identificar qué clase tiene la responsabilidad de hacer este cálculo. Puede descubrir que esta funcionalidad no es requerida globalmente por otras clases y, por lo tanto, puede ser un método privado en 1 clase. Otra cosa a considerar son las definiciones estáticas y la seguridad de los hilos.
NoChance

Respuestas:

9

Probablemente voy a regalar mis inclinaciones de C # de la peor manera posible, pero siempre recomendaría métodos estáticos dentro de las clases de utilidad. Esto te da lo mejor de ambos mundos. Los beneficios organizativos de la clase con la usabilidad de los métodos a los que se accede sin necesidad de instanciar un objeto para usarlos. Si su clase de utilidad tiene una razón para variar ocasionalmente su comportamiento, entonces ya está bien posicionado para hacerlo mediante la introducción de herencia / interfaces más tarde si es necesario. Si desea que su diseño sea un poco más portátil, encontrará que el paradigma se transferirá más fácilmente a otros lenguajes OO.

S.Robins
fuente
"Si su clase de utilidad tiene una razón para variar ocasionalmente su comportamiento": sí, pero estoy en una tienda de Java y no puede cambiar un método estático extendiendo la clase.
S4M
@ S4M Naturalmente, si el comportamiento de su método necesita cambiar, entonces tendrá que repensar el diseño. Sin embargo, como dije en mi respuesta, si necesita modificar el diseño, está bien posicionado para hacerlo. Siempre puede refactorizar el método para que no sea estático si necesita extender el comportamiento. Si tiene todos sus métodos repartidos por todo el lugar, tendrá dificultades para localizarlos. Naturalmente, si el método solo es necesario para una sola clase, entonces sería mejor dejarlo dentro de esa clase, en cuyo caso el problema de ser estático o no se vuelve irrelevante.
S.Robins
En realidad, soy un gran fanático de los métodos estáticos, pero en mi empresa noté que un desarrollador sénior usa métodos no estáticos, así que esto me hizo pensar. Básicamente, quisiera ListBigDecimalUtils.computeAverage (x) mientras que él haría un nuevo AverageListBigDecimal (). Calcular (x). No veo el beneficio de su enfoque.
S4M
@ S4M Puede simplemente permitirle proporcionar a sus métodos acceso a otros métodos no estáticos dentro de su clase. Sin embargo, se admitió que estos podrían ser simplemente métodos estáticos privados adicionales. Quizás lo más probable es que se asegure de dejarse opciones para extender la clase si ve una necesidad en el futuro. Tal vez sea mejor preguntarle a su desarrollador senior cuál es su razonamiento si desea comprender dónde ve los beneficios.
S.Robins
@ s4m. Creo que las clases estáticas simples no misceláneas son buenas y tal vez la mejor opción por ahora (lástima que Java no tenga el método de extensión por cierto). Pero un "servicio de estadísticas" tampoco es una tontería. Me imagino tener la necesidad de cambiar el promedio de la media a la mediana a la media recortada en tiempo de ejecución. Puedo imaginarme operaciones matemáticas en listas de entradas que producen entropía y requerirían un servicio para la comprobabilidad.
Nathan Cooper
5

Si desea realizar funciones de utilidad, conectadas por algún tema, cree una clase para ellas, conviértalas en estáticas en esa clase y haga que un constructor de esa clase sea privado, para eliminar la posibilidad de dar una instancia accidental. Entonces, usa la segunda variante, pero oculta el constructor.

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)

private BigDecimalUtil(){super();}

Hay una posibilidad más: no hacer ningún constructor y hacer que esta clase sea abstracta. Pero de esta manera no puede usar la instancia de la clase no solo fuera, sino también dentro de ella. Entonces, creo que esta segunda variante es demasiado estricta para ti. Elígete a ti mismo.

Gangnus
fuente
4

Estoy de acuerdo con @Gangnus en que una clase de "envoltura" estática tiene sentido si los métodos de clase individuales siempre se utilizarán de forma aislada, y esto a menudo será una buena respuesta.

Sin embargo, puede haber algún valor en una clase no estática, con métodos no estáticos, si tiene cálculos relacionados que puedan usarse juntos. Por ejemplo, la construcción de un ListStatsUtilobjeto con List<BigDecimal>pasado al constructor, que podría ser conveniente para el acceso getStandardDeviation, getVariancey getAverageen secuencia sin pasar de nuevo en el objeto. Esto puede ser más elegante, pero también podría funcionar mejor, especialmente si están involucradas operaciones computacionalmente costosas que pueden optimizarse en todas las operaciones al compartir el estado privado dentro del objeto.

public ListStatsUtil {
    private final List<BigDecimal> numbers;
    public ListStatsUtil(List<BigDecimal> numbers) {
        this.numbers = numbers;
    }
    public BigDecimal getAverage() {
        // return average of this.numbers
    }
    public BigDecimal getStandardDeviation() {
        // return std dev of this.numbers
    }
    public BigDecimal getVariance() {
        // return variance of this.numbers
    }
    public BigDecimal getMax() {
        // return max of this.numbers
    }
    public BigDecimal getMin() {
        // return min of this.numbers
    }
}
codingoutloud
fuente
Todavía no tengo permiso en este nuevo sitio de programadores para dejar un comentario para @Gangnus, así que lo dejaré aquí. ¿Tiene super()sentido el constructor privado para una clase estática en Java?
codingoutloud
Sin duda, tiene sentido solo como una autodisciplina para el caso de una futura refactorización radical. Queremos que este constructor nunca se ejecute.
Gangnus
No entiendo. Todas estas funciones, como getVariance y demás, toman List <BigDecimal> y devuelven BigDecimal de double. ¿Cómo podrías ponerlos en cadena? La idea de encadenar utilidades es buena, pero a) no para el caso proporcionado por usted. b) no en el caso sobre cuál es la pregunta. Es para el caso cuando necesitamos una nueva clase con utilidades . Entonces, esa pregunta nunca surge; la respuesta es obvia.
Gangnus
No en una cadena, solo que es posible que desee múltiples operaciones en la lista única. La pregunta no excluye esta posibilidad.
codingoutloud
La clase que he descrito, puede definir fácilmente tales métodos. Sólo no da ninguna instancia a cabo , pero puede utilizarlos dentro OK. En este caso, por supuesto, el constructor se convierte en uno funcional, pero nos hemos preparado para ello, ¿no? :-)
Gangnus
1

He votado a favor de codificar la respuesta en voz alta y me gustaría ampliarla un poco.

Ajustar colecciones en clases es algo que siempre he intentado hacer: mi regla es "Nunca pasar una colección desnuda". Los mayores fracasos de OO que he visto es cuando las personas tienen miedo de agregar métodos a los que pertenecen. En su caso, los métodos pertenecen a la colección (Totalmente obvio si lo piensa), así que póngalos allí envolviendo la colección y para poner la guinda del pastel, solo exponga la funcionalidad que necesita fuera de su nueva clase (como codingoutloud hizo) para que pueda ver a su clase pequeña y única y comprender fácilmente todo lo que manipula esa colección.

No tiene que resolver todos los problemas posibles porque es todo su código, puede editar el contenedor de colección tan fácilmente como puede editar las otras clases que interactúan con él. Esto le permite incluir otro código en la clase según sea necesario; si nunca expone la colección, se hará rápidamente evidente qué código pertenece en el contenedor.

Esto también le da un control completo sobre la colección para que pueda evitar que entre en un estado inconsistente con su función (por ejemplo, si ninguna entrada en la colección puede ser nula, ¡simplemente evite que alguien ingrese un nulo!)

La mayor parte del código OO incorrecto que he visto se ha creado porque alguien eligió escribir utilidades estáticas o código en línea que pertenecían a objetos a los que no podían agregar código en lugar de envolver los objetos ofensivos en un código propio que podrían modificar .

Bill K
fuente
En este caso particular, diría que el promedio era un método razonable para invocar cualquier colección de números. Por lo tanto, podría terminar dando nuevos nombres a todos sus contenedores de números con un poco más de funcionalidad. Suena como un mal retorno del esfuerzo. Para esto están los métodos de extensión en C #, en Java estás atascado con las estadísticas.
Nathan Cooper
Las utilidades sin lógica de negocios a menudo no tienen una buena solución OO y lo mejor que puede hacer es clases estáticas y clases fijas como colecciones. Si realmente debe hacer una utilidad de propósito general, también podría abandonar las colecciones, la utilidad matemática y todas las demás utilidades. En este caso, probablemente solo esté usando una colección para su código de negocio, entonces, ¿por qué implementar una solución general?
Bill K
0

Si no me equivoco y Java sigue siendo un lenguaje OO puro, no puede tener funciones sin envolverlas en una clase.

Entonces su respuesta es: ambas, una función estática envuelta en una clase de utilidad estática, no muy diferente de la clase Math.

Tulains Córdova
fuente
-2

¿Qué tal una interfaz CalcAverageStrategy que se inyecta, y luego puede tener implementaciones de modo medio / medio / modo inyectadas cuando sea necesario?

Cada implementación es fácil de probar, y también fácil de burlar para probar.

NimChimpsky
fuente
esto se lee más como un comentario, ver Cómo responder
mosquito