Pregunta

¿Está completamente en contra de la forma Java de crear objetos tipo estructura?

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

Puedo ver una clase con accesores y mutadores más parecidos a 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 * d.x) / d.y;
}

Esto no es tan conveniente.

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

Solución

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

En la práctica, la mayoría de los campos tienen captadores y definidores simples.Una posible solución sería la siguiente:

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

Actualizar:Es muy poco probable que se agregue soporte de propiedades en Java 7 o quizás alguna vez.Otros lenguajes JVM como Groovy, Scala, etc. ahora admiten esta función.-Alex Miller

Otros consejos

Parece que muchas personas de Java no están familiarizadas con las pautas de codificación de Java Sun que dicen que es bastante apropiado usar variable de instancia pública cuando la clase es esencialmente una "estructura", si Java admite "estructura" (cuando no hay comportamiento).

La gente tiende a pensar que los captadores y los setters son la forma de Java, como si estuvieran en el corazón de Java.Esto no es así.Si sigue las pautas de codificación Sun Java, utilizando variables de instancia pública en situaciones apropiadas, en realidad está escribiendo un mejor código que desarcharlo con Getters and Setters innecesarios.

Convenciones de código Java de 1999 y aún sin cambios.

10.1 Proporcionar acceso a variables de instancia y clase

No haga pública ninguna instancia o variable de clase sin una buena razón.A menudo, no es necesario establecer u obtener variables de instancia explícitamente; esto suele ocurrir como un efecto secundario de las llamadas a métodos.

Un ejemplo de variables de instancia públicas apropiadas es el caso en el que la clase es esencialmente una estructura de datos, sin comportamiento. En otras palabras, si hubiera usado una estructura en lugar de una clase (si Java admitiera la estructura), entonces 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

Utilice el sentido común de verdad.Si tienes algo como:

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

Entonces no tiene mucho sentido agruparlos en captadores y definidores.Nunca podrás almacenar una coordenada x, y en píxeles completos de otra manera.Los captadores y definidores sólo te ralentizará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 captadores y definidores.

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

Para abordar los problemas de mutabilidad, puede declarar xey como finales.Por ejemplo:

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

Al llamar al código que intenta escribir en estos campos se obtendrá un error en tiempo de compilación del tipo "el campo x se declara final;no puede ser asignado".

El código de cliente puede tener la comodidad "abreviada" que describió en su 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 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}

No utilice public campos

no usar public campos cuando realmente desea ajustar el comportamiento interno de una clase.Llevar java.io.BufferedReader Por ejemplo.Tiene el siguiente campo:

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

skipLF se lee y escribe en todos los métodos de lectura.¿Qué pasaría si una clase externa que se ejecuta en un hilo separado modificara maliciosamente el estado de skipLF en medio de una lectura? BufferedReader Definitivamente se volverá loco.

usar public campos

Toma esto Point clase 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 fuera muy complicado 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 comportamiento más que simples captadores y definidores.Es aceptable utilizar campos públicos cuando la clase representa solo una estructura de datos y no tiene, y nunca tendrá un comportamiento (los captadores y definidores finos son no comportamiento considerado 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(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

¡Limpio!

Pero recuerda:No sólo su clase debe estar ausente de conducta, sino que también debe tener No razón para tener comportamiento en el futuro también.


(Esto es exactamente lo que esta respuesta describe.Citar "Convenciones de código para el lenguaje de programación Java:10.Prácticas de programación":

Un ejemplo de variables de instancia públicas apropiadas es el caso en el que la clase es esencialmente una estructura de datos, sin comportamiento.En otras palabras, si hubieras usado un struct en lugar de una clase (si Java es compatible struct), entonces es apropiado hacer públicas las variables de instancia de la clase.

Entonces la documentación oficial también acepta esta práctica).


Además, si estás muy seguro de que los miembros de arriba Point la clase debe ser inmutable, entonces podrías agregar final palabra clave para hacerla cumplir:

public final double x;
public final double y;

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

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

Re:aku, izb, John Topley...

Cuidado con los problemas de mutabilidad...

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

El problema surge 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.(Piense en lo que pasaría si las cadenas fueran mutables).

Se pone malo cuando ese objeto es una parte importante del estado interno de otro, acabas 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 en GC debido a 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 márquelos como 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 llame excesivamente.

Ver:"Java efectivo" por Joshua Bloch - Artículo #13:Favorecer la inmutabilidad.

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

He probado esto en algunos proyectos, basándose en la teoría de que los captadores y definidores saturan el código con información semántica sin sentido, y que otros lenguajes parecen funcionar bien con la ocultación de datos basada en convenciones o la partición de responsabilidades (por ejemplo,pitón).

Como otros han señalado anteriormente, hay dos problemas con los que te encuentras y que en realidad no se pueden solucionar:

  • Casi cualquier herramienta automatizada en el mundo de Java se basa en la convención getter/setter.Lo mismo ocurre con, como han señalado otros, etiquetas jsp, configuración de primavera, herramientas de eclipse, etc.etc...Luchar contra lo que sus herramientas esperan ver es una receta para largas sesiones de búsqueda en Google tratando de encontrar esa forma no estándar de iniciar Spring Beans.Realmente no vale la pena.
  • Una vez que tenga su aplicación elegantemente codificada con cientos de variables públicas, probablemente encontrará al menos una situación en la que son insuficientes: donde necesita absolutamente la inmutabilidad, o necesita activar algún evento cuando se configura la variable, o desea lanzar una excepción en un cambio de variable porque establece el estado de un objeto en algo desagradable.Luego se verá atrapado con opciones nada envidiables entre saturar su código con algún método especial en todos los lugares donde se hace referencia directa a la variable y tener algún formulario de acceso especial para 3 de las 1000 variables en su aplicación.

Y esto ocurre en el mejor de los casos, trabajando íntegramente en un proyecto privado autónomo.Una vez que exportes 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 método Java es el método OO, entonces sí, crear 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.(Entonces, como no solo le estoy diciendo jerga, un beneficio de ocultar información es que el funcionamiento interno de una clase está oculto detrás de una interfaz; digamos que desea cambiar el mecanismo por el cual su clase de estructura guardó uno de sus campos, probablemente necesitarás regresar y cambiar cualquier clase que use la clase...)

Tampoco puede aprovechar el soporte para clases compatibles con nombres JavaBean, lo que perjudicará si decide, por ejemplo, usar la clase en una página JavaServer escrita usando Expression Language.

El artículo de JavaWorld Por qué los métodos Getter y Setter son malos Este artículo también podría ser de su interés a la hora de pensar en cuándo no implementar métodos de acceso y mutación.

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

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

El problema con el uso del acceso al campo público es el mismo que el uso de un método nuevo en lugar de un método de fábrica: si cambia de opinión más tarde, todos los llamantes existentes quedarán rotos.Entonces, desde el punto de vista de la evolución de la API, generalmente es una buena idea hacer el esfuerzo y usar captadores/definidores.

Un lugar donde voy en sentido contrario 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 resultar mucho más claro utilizar el acceso al campo.

Por cierto, según la afirmación de e-bartek, en mi opinión es muy poco probable que se agregue soporte de propiedad en Java 7.

Con frecuencia 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 puedas hacer que los objetos en tu API pública sean inmutables, mejor, y no es posible construir tu objeto 'tipo estructura' de forma inmutable.

Además, incluso si estuviera escribiendo este objeto como una clase interna privada, todavía 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 sirve es simplemente complicado.

Una pregunta muy, muy antigua, pero permítanme hacer otra breve contribución.Java 8 introdujo expresiones lambda y referencias a métodos.Las expresiones lambda pueden ser referencias a métodos simples y no declarar un cuerpo "verdadero".Pero no se puede "convertir" un campo en una referencia de método.De este modo

stream.mapToInt(SomeData1::x)

no es legal, pero

stream.mapToInt(SomeData2::getX)

es.

No veo ningún daño si sabes que siempre será una estructura simple y que nunca querrás atribuirle un comportamiento.

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

Aquí creo un programa para ingresar el nombre y la edad de 5 personas diferentes y realizar una clasificación de selección (por edad).Utilicé una clase que actúa como estructura (como el lenguaje de programación C) y una clase principal para realizar la operación completa.A continuación les proporciono 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 está probado...Simplemente cópialo y pégalo en tu IDE y...Sabes y que???:)

Puedes crear una clase simple con campos públicos y sin métodos en Java, pero sigue siendo una clase y aún se maneja sintácticamente y en términos de asignación de memoria como una clase.No hay forma de reproducir genuinamente estructuras en Java.

A veces uso dicha clase cuando necesito devolver múltiples valores de un método.Por supuesto, dicho objeto tiene una vida corta y una visibilidad muy limitada, por lo que debería estar bien.

Como ocurre con la mayoría de las cosas, existe la regla general y luego están las circunstancias específicas.Si está realizando una aplicación cerrada y capturada para saber cómo se va a utilizar un objeto determinado, entonces puede ejercer más libertad para favorecer la visibilidad y/o la eficiencia.Si está desarrollando una clase que será utilizada públicamente por otras personas fuera de su control, inclínese hacia el modelo getter/setter.Como ocurre con todas las cosas, utilice el sentido común.A menudo está bien hacer una ronda inicial con los públicos y luego cambiarlos a captadores/definidores más tarde.

La programación orientada a aspectos le permite capturar asignaciones o recuperaciones y adjuntarles lógica de interceptación, lo que propongo es la forma correcta de resolver el problema.(La cuestión de si deben ser públicos, 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 programa, puede adjuntar lógica para quizás validar, hacer una copia del objeto que se devuelve, etc.

La filosofía getter/setter impone costos en un gran número de casos simples en los que no son necesarios.

Si el estilo de aspecto es más limpio o no, es algo cualitativo.Me resultaría fácil ver solo las variables de una clase y ver la lógica por separado.De hecho, la razón de ser de la programación orientada a Apect es que muchas preocupaciones son transversales y compartimentarlas en el cuerpo de la clase en sí no es ideal (el registro es un ejemplo; si desea registrar todos los resultados, Java quiere que lo haga). escribe un montón de captadores y mantenlos sincronizados, pero AspectJ te permite una sola línea).

La cuestión del IDE es una pista falsa.No es tanto la escritura sino la lectura y la contaminación visual que surge de get/sets.

Las anotaciones parecen similares a la programación orientada a aspectos a primera vista, sin embargo, requieren que usted enumere exhaustivamente los puntos de acceso adjuntando anotaciones, a diferencia de una especificación concisa de puntos de acceso tipo comodín en AspectJ.

Espero que el conocimiento de AspectJ impida que las personas se decanten prematuramente por lenguajes dinámicos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top