¿Es esta estructura de código beneficiosa de alguna manera?

8

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á.

Mishelle
fuente
¿Supongo que el estándar de la compañía no dice nada sobre evitar las funciones estáticas? Alternativamente, ¿qué tipo de pruebas automatizadas se realizan con este código? La burla se vuelve casi imposible cuando se introducen funciones estáticas.
KChaloux
3
Solo como una nota: throws Throwablees una mala práctica, debería ser al menos throws 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.
scriptin
El método estático es solo un ayudante para realizar new(...)+ execute()en una llamada.
Kasey Speakman el
Podría cambiarlo de cualquier manera: esta es una solución fea. Sería mejor separar la creación de objetos del consumo.
Thomas Junk
1
También tenga en cuenta que el autor obviamente pretendía que el objeto construido sea inmutable (los datos son 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 de executeun método estático teniendo p1, p2, ....
Kasey Speakman

Respuestas:

2

Creo que su intuición sobre este tema es correcta.

  • Lo que tenemos aquí es un método simple (en este caso, estático) cuya implementación se ha "dividido" en una clase.
  • La técnica del "método como clase" reduce el número de variables que tiene que pasar explícitamente, mientras que en realidad oculta cómo fluyen los datos utilizando, en esencia, variables "globales".
  • Nada de esto está realmente en el espíritu de OO, ya que la instancia es desechable y su estado es efímero.
  • Y, sin embargo, este es un patrón común, especialmente en los libros introductorios de Java y puede haber estado en la mente de los diseñadores de Java (quienes introdujeron muchas formas de declarar clases, aparentemente para ayudar a este uso).

Entonces, ¿deberías usarlo ?

  • Claro, a veces.

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.

Aleksandr Dubinsky
fuente
1
Me gusta lo que estás sugiriendo en el último párrafo aquí. Si el método execute () fuera público, podría crear (e inicializar) el objeto, y luego hacer que algo lo ejecute más tarde. Eso podría tener sentido y también mejoraría la capacidad de prueba. En ese caso, es realmente la función estática lo que es extraño.
Mishelle
Esta es la respuesta que esta pregunta ha estado esperando. +1
cbojar
6

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
1
¿Dónde ves un método de fábrica? Ninguno de los ejemplos del OP devuelve nada.
Robert Harvey
@RobertHarvey: es efectivamente un método de fábrica envuelto. ¡El hecho de que no esté expuesto al sitio de llamadas no significa que no sea una fábrica!
Carreras de ligereza en órbita el
1
@RobertHarvey Creo que es una cuestión de elección de palabras. Se comporta de manera similar a un método de fábrica, pero en realidad no devuelve nada. Por otra parte, nunca dije que lo hiciera, solo que delegó, lo que sigue siendo una declaración precisa.
Estoy de acuerdo con @RobertHarvey. Un objeto construido y utilizado completamente dentro de un método es un detalle de implementación, no un producto de fábrica.
cbojar
Interesante. Modifiqué la terminología en mi respuesta, pero el punto que hice es idéntico. Extraño cómo sucedió eso ...
0

Bueno, si su segundo ejemplo (que muestra el código completo ) se ve así:

public static void doSomething(int p1, String p2, …) throws Throwable     {
      final MyThingy myThingy = new MyThingy(p1, p2, …);
      myThingy.execute();
   }

entonces todavía vas a necesitar el constructor

public MyThingy (int p1, String p2, …) {
      this.p1 = p1;
      this.p2 = p2;
      
   }

que a su vez establece

   private final int p1;
   private final String p2;

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

private final int p1;
private final String p2;

public void SetP1(int p1)
{
    this.p1 = p1;
}

public int GetP1()
{
    return p1;
}
// repeat for p2

... 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.

Robert Harvey
fuente
No tiene sentido tener un setter público en una finalvariable. Solo se puede inicializar una vez.
Kasey Speakman el
1
@KaseySpeakman: Ergo, todavía necesitas un constructor.
Robert Harvey, el
No estoy completamente seguro de que esta respuesta responda a la pregunta ...
cbojar
Además, no es válido (tiene un error de compilación): ideone.com/0nDRgY --- el valor final debe poder demostrarse que no se puede configurar en ningún otro lugar. Tener un setter lo hace imposible, y por lo tanto un error de compilación.
@MichaelT: Ya dije eso en mi respuesta (en el último párrafo).
Robert Harvey
0

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.

gnasher729
fuente
Creo que el OP dice que no hay otros métodos de instancia además execute. Así preguntando por qué no en línea todo el asunto.
cbojar
Sí, esa es exactamente mi pregunta!
Mishelle