Dominar las clases de 'funciones de utilidad'

14

En nuestro código base de Java, sigo viendo el siguiente patrón:

/**
 This is a stateless utility class
 that groups useful foo-related operations, often with side effects.
*/
public class FooUtil {
  public int foo(...) {...}
  public void bar(...) {...}
}

/**
 This class does applied foo-related things.
*/
class FooSomething {
  int DoBusinessWithFoo(FooUtil fooUtil, ...) {
    if (fooUtil.foo(...)) fooUtil.bar(...);
  }
}

Lo que me molesta es que tengo que pasar una instancia de FooUtil en todas partes, porque las pruebas.

  • No puedo hacer que los métodos de FooUtil sean estáticos, porque no podré burlarme de ellos para probar tanto FooUtil como sus clases de clientes.
  • No puedo crear una instancia de FooUtil en el lugar de consumo con un new , otra vez porque no podré burlarme de la prueba.

Supongo que mi mejor apuesta es usar la inyección (y yo lo hago), pero agrega su propio conjunto de problemas. Además, pasar varias instancias de utilidad infla el tamaño de las listas de parámetros del método.

¿Hay una manera de manejar esto mejor que no estoy viendo?

Actualizar: dado que las clases de utilidad no tienen estado, probablemente podría agregar un miembro singleton INSTANCE estático, o un método estático getInstance() , mientras conservo la capacidad de actualizar el campo estático subyacente de la clase para la prueba. No parece muy limpio, también.

    
pregunta 9000 05.01.2015 - 21:47

2 respuestas

13

En primer lugar, permítame decir que hay diferentes enfoques de programación para diferentes problemas. Para mi trabajo, prefiero un diseño fuerte de OO para la organización y el mantenimiento, y mi respuesta lo refleja. Un enfoque funcional tendría una respuesta muy diferente.

Tiendo a encontrar que la mayoría de las clases de utilidad se crean porque el objeto en el que deberían estar los métodos no existe. Esto casi siempre proviene de uno de dos casos, ya sea que alguien está pasando las colecciones o alguien está pasando por los frijoles (a menudo se llaman POJOs, pero creo que un grupo de setters y getters se describe mejor como un bean).

Cuando tienes una colección que estás transmitiendo, debe haber un código que quiera ser parte de esa colección, y esto termina siendo esparcido por todo tu sistema y, finalmente, recopilado en clases de utilidad.

Mi sugerencia es envolver sus colecciones (o beans) con cualquier otro dato estrechamente asociado en nuevas clases y colocar el código de "utilidad" en los objetos reales.

Puede ampliar la colección y agregar los métodos allí, pero pierde el control del estado de su objeto de esa manera. Le sugiero que lo complete por completo y solo exponga los métodos de lógica de negocios que sean necesarios (piense "No pregunte por sus objetos. sus datos, asigne comandos a sus objetos y déjelos actuar sobre sus datos privados ", un inquilino OO importante y uno que, cuando lo piensa, requiere el enfoque que sugiero aquí).

Este "ajuste" es útil para cualquier objeto de biblioteca de propósito general que contenga datos, pero no puede agregar código. Poner código con los datos que manipula es un concepto importante en el diseño OO.

Hay muchos estilos de codificación en los que este concepto de ajuste sería una mala idea. ¿Quién quiere escribir una clase completa cuando solo se implementa un script o prueba de una sola vez? Pero creo que la encapsulación de cualquier tipo / colección básica que no puedas agregarte a ti mismo es obligatoria para OO.

    
respondido por el Bill K 06.01.2015 - 20:38
1

Podría usar un patrón de Proveedor e inyectar su FooSomething con un objeto FooUtilProvider (podría estar en un constructor; solo una vez).

FooUtilProvider solo tendría un método: FooUtils get(); . La clase usaría la instancia FooUtils que proporciona.

Ahora que está a un paso de usar un marco de inyección de dependencia, cuando solo tendrá que conectar el contenedor de monedas DI dos veces: para el código de producción y para su conjunto de pruebas. Simplemente vincula la interfaz FooUtils a RealFooUtils o MockedFooUtils , y el resto sucede automáticamente. Cada objeto que depende de FooUtilsProvider obtiene la versión correcta de FooUtils .

    
respondido por el Konrad Morawski 07.01.2015 - 01:43

Lea otras preguntas en las etiquetas