Recientemente me lanzaron a un proyecto de aplicación web Java, y me he encontrado con varias clases que siguen este tipo de formato:
public class MyThingy {
private final int p1;
private final String p2;
…
public MyThingy (int p1, String p2, …) {
this.p1 = p1;
this.p2 = p2;
…
}
public static void doSomething(int p1, String p2, …) throws Throwable {
final MyThingy myThingy = new MyThingy(p1, p2, …);
myThingy.execute();
}
private void execute() throws Throwable {
//do stuff
}
}
Parece que esto podría lograrse con el siguiente código, que me parece mucho más fácil de leer.
public class MyThingy {
public static void doSomething (int p1, String p2, …) throws Throwable {
//do stuff
}
}
El único beneficio posible que puedo ver al hacerlo de la primera manera es que si tuviera que dividir execute () en partes más pequeñas, todos podrían compartir los parámetros iniciales sin tener que pasarlos explícitamente. Pero esto tal vez solo beneficie al codificador perezoso, ya que se vuelve difícil para el lector decir qué métodos necesitan qué parámetros y cuándo se pueden cambiar los valores (similares a las variables globales).
¿Se me escapa algo? Roscado, rendimiento?
Editar: Debería haber mencionado, aunque el constructor es público, no se llama. El único uso es así:
MyThingy.doSomething(p1, p2...);
Además de que esto en sí mismo es problemático para las pruebas, no veo ninguna razón para no poner la lógica de execute () directamente en doSomething (). Incluso si tuviéramos que deshacernos de la función estática, el constructor todavía no tiene sentido para mí; Creo que los parámetros deberían pasarse directamente al método que los usará.
fuente
throws Throwable
es una mala práctica, debería ser al menosthrows Exception
, o algo más específico, si es posible. Y, dado que las malas prácticas generalmente se unen, diría que esta plantilla de código es solo otra mala práctica.new(...)
+execute()
en una llamada.final
). El método estático es la única API en el objeto ya que todos los miembros son privados. En sólo el código que has compartido, no veo ningún beneficio a este sobre la toma deexecute
un método estático teniendo p1, p2, ....Respuestas:
Creo que su intuición sobre este tema es correcta.
Entonces, ¿deberías usarlo ?
De hecho, no se trata solo de "métodos como clases" puros. En las clases normales, puede sentirse tentado a crear campos que no contengan invocaciones intermedias de métodos públicos. Se usan solo para evitar pasar parámetros entre métodos privados. En el pasado, traté de evitarlo a toda costa, pero luego me di cuenta de que las largas listas de parámetros también apestan.
Intento evitar las "clases de método" puras, ya que creo que vienen con mucho equipaje mental (tanto potencial potencial). (También me apoyo en los campos desechables en las clases regulares). Pero si hay un montón de variables para repartir, la victoria en la limpieza del código puede valer la pena.
Intento usar clases cuando hay un punto, y de hecho, generalmente es fácil imaginar una. Quizás en el futuro extienda la clase con implementaciones adicionales. Quizás la instancia pueda considerarse como un objeto de función que desea inicializar y transmitir. Y luego, la clase ya no es una "clase de método" pseudo-OO.
fuente
La indirección adicional a través de la llamada al método estático separa las preocupaciones de cómo se crea un objeto del código que lo utiliza. Lo que tiene aquí es algo muy similar a un método simple de fábrica (en realidad no devuelve el objeto, pero la indirecta de la creación del objeto es la misma).
Esto puede ser útil si necesita controlar el tipo de objeto que se crea. En este ejemplo simple, no se debe tomar una decisión, pero sería fácil agregar esa lógica.
Lo que el código estático significa para las personas que llaman es "Necesito hacer algo con este estado pero no sé cómo delegar". El método estático puede delegar a la implementación adecuada en función del objeto proporcionado.
Quizás cuando se ejecuta en una prueba unitaria se está utilizando un objeto simulado. Tal vez en función de los parámetros, se podría intercambiar un objeto diferente que implemente un algoritmo diferente para hacer lo que sea necesario. Al localizar ese código en una ubicación, la decisión se puede tomar en un lugar en lugar de arbitrariamente en muchos.
Dado que su pregunta está redactada y el hecho de que no se está tomando una decisión de construcción en los métodos estáticos, tengo la sensación de que esto podría ser solo un caso de ingeniería excesiva. La mayoría del código no necesita delegarse en una fábrica. El hecho de que mantenga encontrarse con un código como el que no , no toma ninguna decisión con lo que cabe esperar de una fábrica estática como método para hacerlo me dice alguien que lea el libro GoF y fuera de control RAN.
fuente
Bueno, si su segundo ejemplo (que muestra el código completo ) se ve así:
entonces todavía vas a necesitar el constructor
que a su vez establece
y ahora estás de vuelta donde empezaste.
Podría, por supuesto, simplemente eliminar el constructor e ir con getters y setters en su lugar
... pero esto complicará su llamada al método estático, ya que ahora necesitará varias líneas de código para llamar primero a los establecedores antes de llamar a su método estático, donde habría bastado una sola línea haciendo uso de la llamada del constructor.
Por supuesto, esto solo funciona si sus variables miembro no lo son
final
. Por lo tanto, aún necesitarás el constructor.fuente
final
variable. Solo se puede inicializar una vez.Por lo general, una clase tiene muchos métodos de instancia, y una vez que se crea el objeto, la implementación de execute puede aprovechar todos estos métodos de instancia. Si se niega a crear un objeto, todos estos métodos de instancia son inútiles.
fuente
execute
. Así preguntando por qué no en línea todo el asunto.