Construir objetos similares en Java

¿Está completamente en contra de la forma de Java de crear objetos similares a estructuras?

class SomeData1 { public int x; public int y; } 

Puedo ver una clase con accesodores y mutadores que son más como Java.

 class SomeData2 { int getX(); void setX(int x); int getY(); void setY(int y); private int x; private int y; } 

La clase del primer ejemplo es notablemente conveniente.

 // a function in a class public int f(SomeData1 d) { return (3 * dx) / dy; } 

Esto no es tan conveniente.

 // a function in a class public int f(SomeData2 d) { return (3 * d.getX()) / d.getY(); } 

Este es un tema comúnmente discutido. El inconveniente de crear campos públicos en objetos es que no tiene control sobre los valores que se le asignan. En proyectos grupales donde hay muchos progtwigdores que utilizan el mismo código, es importante evitar los efectos secundarios. Además, a veces es mejor devolver una copia del objeto del campo o transformarlo de alguna manera, etc. Puedes simular dichos métodos en tus pruebas. Si creas una nueva clase, es posible que no veas todas las acciones posibles. Es como una progtwigción defensiva: algunos captadores y establecedores algún día pueden ser útiles, y no cuesta mucho crearlos / usarlos. Entonces a veces son útiles.

En la práctica, la mayoría de los campos tienen getters y setters simples. Una posible solución se vería así:

 public property String foo; a->Foo = b->Foo; 

Actualización: es muy poco probable que se agregue soporte de propiedad en Java 7 o quizás nunca. Otros lenguajes JVM como Groovy, Scala, etc. ahora son compatibles con esta característica. – Alex Miller

Parece que muchas personas de Java no están familiarizadas con las Pautas de encoding de Sun Java que dicen que es bastante apropiado usar una variable de instancia pública cuando la clase es esencialmente una “estructura”, si es compatible con “estructura” de Java (cuando no hay comportamiento).

La gente tiende a pensar que los getters y setters son de Java, como si estuvieran en el corazón de Java. Esto no es asi Si sigue las Pautas de encoding de Sun Java, utilizando variables de instancias públicas en situaciones apropiadas, en realidad está escribiendo un código mejor que atiborrándolo con getters y setters innecesarios.

Convenciones de Java Code desde 1999 y aún sin cambios.

10.1 Proporcionar acceso a instancia y variables de clase

No haga pública ninguna instancia o clase de variable sin una buena razón. A menudo, las variables de instancia no necesitan establecerse ni obtenerse explícitamente; a menudo eso sucede como un efecto secundario de las llamadas a métodos.

Un ejemplo de variables de instancias públicas apropiadas es el caso donde la clase es esencialmente una estructura de datos, sin comportamiento. En otras palabras, si hubiera utilizado una estructura en lugar de una clase (si la estructura admitida por Java), es apropiado hacer públicas las variables de instancia de la clase .

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28

Use el sentido común realmente. Si tiene algo como:

 public class ScreenCoord2D{ public int x; public int y; } 

Entonces, no tiene sentido encerrarlos en getters y setters. Nunca va a almacenar una coordenada x, y en píxeles enteros de ninguna otra manera. Getters y setters solo lo retrasarán.

Por otro lado, con:

 public class BankAccount{ public int balance; } 

Es posible que desee cambiar la forma en que se calcula el saldo en algún momento en el futuro. Esto realmente debería usar getters y setters.

Siempre es preferible saber por qué está aplicando buenas prácticas, para que sepa cuándo está bien doblar las reglas.

Para abordar los problemas de mutabilidad, puede declarar x e y como definitivo. Por ejemplo:

 class Data { public final int x; public final int y; public Data( int x, int y){ this.x = x; this.y = y; } } 

El código de llamada que intenta escribir en estos campos obtendrá un error de tiempo de comstackción de “campo x declarado final, no puede ser asignado”.

El código del cliente puede tener la conveniencia de ‘mano corta’ que describiste en tu publicación

 public class DataTest { public DataTest() { Data data1 = new Data(1, 5); Data data2 = new Data(2, 4); System.out.println(f(data1)); System.out.println(f(data2)); } public int f(Data d) { return (3 * dx) / dy; } public static void main(String[] args) { DataTest dataTest = new DataTest(); } } 

Re: aku, izb, John Topley …

Cuidado con los problemas de mutabilidad …

Puede parecer sensato omitir getters / setters. De hecho, puede estar bien en algunos casos. El verdadero problema con el patrón propuesto que se muestra aquí es la mutabilidad.

El problema es una vez que pasa una referencia de objeto que contiene campos públicos no finales. Cualquier otra cosa con esa referencia es libre de modificar esos campos. Ya no tienes ningún control sobre el estado de ese objeto. (Piensa qué pasaría si las cadenas fueran mutables).

Se pone mal cuando ese objeto es una parte importante del estado interno de otro, acaba de exponer la implementación interna. Para evitar esto, se debe devolver una copia del objeto. Esto funciona, pero puede causar una presión masiva de GC de toneladas de copias de un solo uso creadas.

Si tiene campos públicos, considere hacer que la clase sea de solo lectura. Agregue los campos como parámetros al constructor y marque los campos finales. De lo contrario, asegúrese de no exponer el estado interno, y si necesita construir nuevas instancias para un valor de retorno, asegúrese de que no se invoque en exceso.

Ver: ” Java efectivo ” por Joshua Bloch – Artículo n. ° 13: Favorezca la inmutabilidad.

PD: También tenga en cuenta que, en estos días, todas las JVM optimizarán getMethod si es posible, lo que dará como resultado una sola instrucción de lectura de campo.

No use campos public

No use campos public cuando realmente quiera ajustar el comportamiento interno de una clase. Tome java.io.BufferedReader por ejemplo. Tiene el siguiente campo:

 private boolean skipLF = false; // If the next character is a line feed, skip it 

skipLF es leído y escrito en todos los métodos de lectura. ¿Qué skipLF si una clase externa que se ejecuta en un subproceso diferente modificó maliciosamente el estado de skipLF en medio de una lectura? BufferedReader definitivamente se volverá loco.

Utiliza campos public

Tome esta clase Point por ejemplo:

 class Point { private double x; private double y; public Point(double x, double y) { this.x = x; this.y = y; } public double getX() { return this.x; } public double getY() { return this.y; } public void setX(double x) { this.x = x; } public void setY(double y) { this.y = y; } } 

Esto haría que calcular la distancia entre dos puntos sea muy doloroso de escribir.

 Point a = new Point(5.0, 4.0); Point b = new Point(4.0, 9.0); double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2)); 

La clase no tiene ningún otro comportamiento que los get y setters simples. Es aceptable usar campos públicos cuando la clase representa solo una estructura de datos, y no tiene, y nunca tendrá, un comportamiento (los captadores y los instaladores delgados no se consideran comportamientos aquí). Se puede escribir mejor de esta manera:

 class Point { public double x; public double y; public Point(double x, double y) { this.x = x; this.y = y; } } Point a = new Point(5.0, 4.0); Point b = new Point(4.0, 9.0); double distance = Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); 

¡Limpiar!

Pero recuerde: no solo su clase debe estar ausente de su comportamiento, sino que tampoco debería tener ninguna razón para tener un comportamiento en el futuro.

Por cierto, la estructura que está dando como ejemplo ya existe en la biblioteca de clases base de Java como java.awt.Point . Tiene xey como campos públicos, compruébalo tú mismo .

Si sabes lo que estás haciendo, y otros en tu equipo lo saben, entonces está bien tener campos públicos. Pero no debes confiar en ello porque pueden causar dolores de cabeza como en errores relacionados con desarrolladores que usan objetos como si fueran estructuras asignadas en stack (los objetos Java siempre se envían a métodos como referencias y no como copias).

Lo he intentado en algunos proyectos, bajo la teoría de que los captadores y decodificadores complican el código con cruces semánticamente sin sentido, y que a otros lenguajes les va muy bien con la ocultación de datos basada en convenciones o la división de responsabilidades (por ejemplo, python).

Como otros han mencionado anteriormente, hay 2 problemas con los que se encuentra, y no son realmente corregibles:

  • Casi cualquier herramienta automatizada en el mundo de Java se basa en la convención getter / setter. Lo mismo ocurre con otras tags jsp, configuración de spring, herramientas de eclipse, etc., etc. Luchando contra lo que esperan ver sus herramientas es una receta para largas sesiones de búsqueda de Google tratando de encontrar esa forma no estándar de iniciar frijoles de spring. Realmente no vale la pena el problema.
  • Una vez que tenga su aplicación elegantemente codificada con cientos de variables públicas, es probable que encuentre al menos una situación en la que son insuficientes, donde absolutamente necesita inmutabilidad, o necesita desencadenar algún evento cuando la variable se establece, o si desea lanzar una excepción en un cambio de variable porque establece un estado de objeto como algo desagradable. Entonces está atascado con las opciones poco envidiables entre saturar su código con algún método especial donde quiera que se haga referencia directa a la variable, teniendo algún formulario de acceso especial para 3 de las 1000 variables en su aplicación.

Y este es el mejor escenario para trabajar completamente en un proyecto privado independiente. Una vez que exporta todo a una biblioteca de acceso público, estos problemas serán aún mayores.

Java es muy detallado, y esto es algo tentador de hacer. No lo hagas

Si el modo Java es el modo OO, entonces sí, la creación de una clase con campos públicos rompe los principios sobre el ocultamiento de información que dicen que un objeto debe administrar su propio estado interno. (Así que como no solo te estoy lanzando una jerga, un beneficio del ocultamiento de información es que el funcionamiento interno de una clase está oculto detrás de una interfaz; digamos que querías cambiar el mecanismo por el cual tu clase de estructura guardaba uno de sus campos, es probable que necesites volver atrás y cambiar las clases que usan la clase …)

Tampoco puede aprovechar el soporte para las clases que cumplen los requisitos JavaBean, lo que le dolerá si decide, por ejemplo, usar la clase en una página JavaServer que está escrita usando el lenguaje de expresión.

El artículo de JavaWorld Why Getter and Setter Methods es un artículo de Evil que también podría ser de su interés al pensar cuándo no implementar los métodos de acceso y mutador.

Si está escribiendo una pequeña solución y desea minimizar la cantidad de código involucrado, la forma de Java puede no ser la correcta, supongo que siempre depende de usted y del problema que está tratando de resolver.

No hay nada de malo con ese tipo de código, siempre que el autor sepa que son estructuras (o transferencias de datos) en lugar de objetos. Muchos desarrolladores de Java no pueden distinguir entre un objeto bien formado (no solo una subclase de java.lang.Object, sino un objeto verdadero en un dominio específico) y una piña. Ergo, terminan escribiendo estructuras cuando necesitan objetos y viceversa.

El problema con el uso de acceso de campo público es el mismo problema que usar un método nuevo en lugar de un método de fábrica: si cambia de opinión más adelante, todas las personas que llaman se rompen. Entonces, desde el punto de vista de la evolución de la API, generalmente es una buena idea morder la bala y usar getters / setters.

Un lugar al que voy por el otro lado es cuando controlas fuertemente el acceso a la clase, por ejemplo, en una clase estática interna utilizada como estructura de datos interna. En este caso, podría ser mucho más claro usar el acceso de campo.

Por cierto, en la afirmación de e-bartek, es altamente improbable que la OMI se agregue soporte de propiedad en Java 7.

Frecuentemente uso este patrón cuando construyo clases internas privadas para simplificar mi código, pero no recomendaría exponer dichos objetos en una API pública. En general, cuanto más frecuentemente pueda hacer que los objetos de su API pública sean inmutables, mejor será, y no es posible construir su objeto ‘struct-like’ de manera inmutable.

Como comentario adicional, incluso si estuviera escribiendo este objeto como una clase interna privada, aún así proporcionaría un constructor para simplificar el código para inicializar el objeto. Tener que tener 3 líneas de código para obtener un objeto utilizable cuando uno lo hace es simplemente desordenado.

Una pregunta muy, muy vieja, pero permítanme hacer otra pequeña contribución. Java 8 introdujo expresiones lambda y referencias de métodos. Las expresiones lambda pueden ser simples referencias a métodos y no declarar un cuerpo “verdadero”. Pero no puede “convertir” un campo en una referencia de método. Así

 stream.mapToInt(SomeData1::x) 

no es legal, pero

 stream.mapToInt(SomeData2::getX) 

es.

No veo el daño si sabes que siempre va a ser una estructura simple y que nunca querrás vincular tu comportamiento.

Esta es una pregunta sobre el diseño orientado a objetos, no el lenguaje Java. En general, es una buena práctica ocultar tipos de datos dentro de la clase y exponer solo los métodos que forman parte de la clase API. Si expone tipos de datos internos, nunca podrá cambiarlos en el futuro. Si los oculta, su única obligación para el usuario son los tipos de retorno y argumento del método.

Aquí creo un progtwig para ingresar el nombre y la edad de 5 personas diferentes y realizar un tipo de selección (según la edad). Usé una clase que actúa como una estructura (como el lenguaje de progtwigción C) y una clase principal para realizar la operación completa. A continuación estoy suministrando el código …

 import java.io.*; class NameList { String name; int age; } class StructNameAge { public static void main(String [] args) throws IOException { NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object NameList temp=new NameList(); // Create a temporary object of the structure BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); /* Enter data into each radix of 'nl' object */ for(int i=0; i<5; i++) { nl[i]=new NameList(); // Assign the structure into each radix System.out.print("Name: "); nl[i].name=br.readLine(); System.out.print("Age: "); nl[i].age=Integer.parseInt(br.readLine()); System.out.println(); } /* Perform the sort (Selection Sort Method) */ for(int i=0; i<4; i++) { for(int j=i+1; j<5; j++) { if(nl[i].age>nl[j].age) { temp=nl[i]; nl[i]=nl[j]; nl[j]=temp; } } } /* Print each radix stored in 'nl' object */ for(int i=0; i<5; i++) System.out.println(nl[i].name+" ("+nl[i].age+")"); } } 

El código anterior está libre de errores y probado ... Solo cópielo y péguelo en su IDE y ... ¿Sabes qué? 🙂

Puede hacer una clase simple con campos públicos y sin métodos en Java, pero sigue siendo una clase y todavía se maneja sintácticamente y en términos de asignación de memoria al igual que una clase. No hay forma de reproducir estructuras genuinamente en Java.

En algún momento utilizo dicha clase, cuando necesito devolver múltiples valores de un método. Por supuesto, dicho objeto es de corta duración y con visibilidad muy limitada, por lo que debería estar bien.

Como con la mayoría de las cosas, existe la regla general y luego hay circunstancias específicas. Si está haciendo una aplicación cerrada y capturada para que sepa cómo se va a utilizar un determinado objeto, puede ejercer más libertad para favorecer la visibilidad y / o la eficiencia. Si está desarrollando una clase que va a utilizar públicamente alguien ajeno a su control, inclínese hacia el modelo getter / setter. Como con todas las cosas, solo usa el sentido común. A menudo está bien hacer una ronda inicial con públicos y luego cambiarlos a getter / setters más tarde.

La progtwigción orientada a aspectos le permite interceptar asignaciones o recuperaciones y adjuntar lógica de interceptación, que propongo es la forma correcta de resolver el problema. (La cuestión de si deben ser públicos o protegidos o protegidos por paquetes es ortogonal).

Por lo tanto, comienza con campos no interceptados con el calificador de acceso correcto. A medida que crecen los requisitos de su progtwig, añada la lógica para quizás validar, hacer una copia del objeto que se devuelve, etc.

La filosofía getter / setter impone costos en una gran cantidad de casos simples donde no son necesarios.

Si el estilo de aspecto es más limpio o no es algo cualitativo. Me sería fácil ver solo las variables en una clase y ver la lógica por separado. De hecho, la razón de ser de la progtwigción orientada a Apect es que muchas preocupaciones son transversales y compartimentarlas en el cuerpo de la clase en sí mismo no es ideal (el registro es un ejemplo; si quieres iniciar sesión, todo lo que Java quiere es que lo hagas). escribe un montón de captadores y mantenlos sincronizados, pero AspectJ te permite un trazador de líneas único).

El problema de IDE es un misterio. No se trata tanto de la tipificación como de la lectura y la contaminación visual que surge de get / sets.

Las anotaciones parecen similares a la progtwigción orientada a aspectos a primera vista; sin embargo, requieren que enumere puntos de manera exhaustiva adjuntando anotaciones, a diferencia de una especificación concisa de corte de puntos similar a la de comodín en AspectJ.

Espero que la conciencia de AspectJ impida a las personas establecerse de forma prematura en lenguajes dynamics.