Hace un par de meses comencé a trabajar en un nuevo proyecto, y cuando revisé el código me sorprendió la cantidad de métodos estáticos utilizados. No solo se utilizan métodos de utilidad collectionToCsvString(Collection<E> elements)
, sino también mucha lógica de negocios.
Cuando le pregunté al tipo responsable de la razón detrás de esto, dijo que era una forma de escapar de la tiranía de Spring . Va en torno a este proceso de pensamiento: para implementar un método de creación de recibo del cliente, podríamos tener un servicio
@Service
public class CustomerReceiptCreationService {
public CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Ahora, el tipo dijo que no le gusta que Spring administre clases innecesariamente, básicamente porque impone la restricción de que las clases de clientes deben ser Spring beans. Terminamos teniendo todo administrado por Spring, lo que nos obliga a trabajar con objetos sin estado de una manera procesal. Más o menos lo que se indica aquí https://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Entonces, en lugar del código anterior, tiene
public class CustomerReceiptCreator {
public static CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Podría argumentar hasta el punto de evitar que Spring administre nuestras clases cuando sea posible, pero lo que no veo es el beneficio de tener todo estático. Estos métodos estáticos también son apátridas, por lo que tampoco son muy OO. Me sentiría más cómodo con algo como
new CustomerReceiptCreator().createReceipt()
Afirma que los métodos estáticos tienen algunos beneficios adicionales. A saber:
- Más fácil de leer. Importe el método estático y solo debemos preocuparnos por la acción, sin importar qué clase lo esté haciendo.
- Obviamente, es un método libre de llamadas a DB, por lo que es económico en términos de rendimiento; y es bueno dejarlo claro, para que el cliente potencial necesite ingresar al código y verificarlo.
- Más fácil de escribir pruebas.
Pero siento que hay algo que no está del todo bien con esto, por lo que me gustaría escuchar algunos pensamientos más experimentados de los desarrolladores sobre esto.
Entonces mi pregunta es, ¿cuáles son las posibles trampas de esta forma de programación?
fuente
static
método que está ilustrando arriba es solo un método de fábrica común. Hacer que los métodos de fábrica sean estáticos es la convención generalmente aceptada, por varias razones convincentes. Si el método de fábrica es apropiado aquí es una cuestión diferente.Respuestas:
¿Cuál es la diferencia entre
new CustomerReceiptCreator().createReceipt()
yCustomerReceiptCreator.createReceipt()
? Casi ninguno. La única diferencia significativa es que el primer caso tiene una sintaxis considerablemente más incómoda. Si sigue lo primero en la creencia de que de alguna manera evitar los métodos estáticos hace que su código sea mejor OO, está gravemente equivocado. Crear un objeto solo para llamar a un método único en él es un método estático por sintaxis obtusa.Donde las cosas se ponen diferentes es cuando se inyecta en
CustomerReceiptCreator
lugar de inyectarlonew
. Consideremos un ejemplo:Comparemos esto con una versión de método estático:
La ventaja de la versión estática es que puedo decirte fácilmente cómo interactúa con el resto del sistema. No lo hace. Si no he usado variables globales, sé que el resto del sistema no ha cambiado de alguna manera. Sé que ninguna otra parte del sistema podría estar afectando el recibo aquí. Si he usado objetos inmutables, sé que el orden no ha cambiado, y createReceipt es una función pura. En ese caso, puedo mover / eliminar / etc. libremente esta llamada sin preocuparme por los efectos aleatorios impredecibles en otros lugares.
No puedo hacer las mismas garantías si me he inyectado el
CustomerReceiptCreator
. Es posible que tenga un estado interno modificado por la llamada, podría estar afectado o cambiar otro estado. Puede haber relaciones impredecibles entre declaraciones en mi función, de modo que cambiar el orden introducirá errores sorprendentes.Por otro lado, ¿qué sucede si de
CustomerReceiptCreator
repente necesita una nueva dependencia? Digamos que necesita verificar un indicador de función. Si estuviéramos inyectando, podríamos hacer algo como:Entonces hemos terminado, porque el código de llamada se inyectará con un
CustomerReceiptCreator
que se inyectará automáticamente aFeatureFlags
.¿Y si estuviéramos usando un método estático?
¡Pero espera! el código de llamada también necesita ser actualizado:
Por supuesto, esto todavía deja la cuestión de dónde
processOrder
obtiene su origenFeatureFlags
. Si tenemos suerte, el camino termina aquí, si no, la necesidad de pasar por FeatureFlags se ve empujada más arriba en la pila.Hay una compensación aquí. Métodos estáticos que requieren pasar explícitamente las dependencias, lo que resulta en más trabajo. El método inyectado reduce el trabajo, pero hace que las dependencias sean implícitas y, por lo tanto, ocultas, lo que hace que el código sea más difícil de razonar.
fuente