¿Por qué Java no utiliza la encapsulación con algunas clases?

25

Mi pregunta está relacionada con System.iny las System.outclases (puede haber otros como los de la biblioteca estándar). ¿Porqué es eso? ¿No es una mala práctica en OOP? ¿No debería usarse como: System.getIn()y System.getOut()? Siempre he tenido esta pregunta y espero encontrar una buena respuesta aquí.

Clawdidr
fuente

Respuestas:

36

La definición de los campos iny outdentro de la Systemclase son:

public final static PrintStream out;
public final static InputStream in;

Estas son constantes. También son objetos, pero son constantes. Es muy similar a la clase de matemáticas:

public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;

O en la clase booleana:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

O en la clase de color:

public final static Color white     = new Color(255, 255, 255);
public final static Color black     = new Color(0, 0, 0);
public final static Color red       = new Color(255, 0, 0);

Al acceder a una constante pública que no cambia, no hay una ventaja significativa para encapsularla, ya sea conceptualmente o en función del rendimiento. Está allá. No va a cambiar

No hay una diferencia real entre Color.whitey System.out.


fuente
Bueno, no lo he visto así, son objetos constantes. Esa es una muy buena explicación.
Clawdidr
1
@Clawdidr en el Java de hoy, uno podría considerar usar el enumpara mantenerlos ... aunque enumera nuevo con Java 1.5 (no era una opción en los 1.0 días).
Solo por curiosidad (no soy un desarrollador de Java): ¿hay alguna diferencia entre final staticy static final? Si es así, ¿qué es?
Marjan Venema el
1
@MarjanVenema no hay diferencia, solo el orden preferido de los modificadores. checkstyle modifier check muestra el orden preferido. Aparentemente, quien escribió esos dos archivos de origen en la versión jdk no estuve de acuerdo con el pedido. Es simplemente una convención.
:) y gracias Michael, por tomarse el tiempo para responder y por el enlace.
Marjan Venema
5

La verdadera razón es que este es un problema heredado. Las System.in,out,errconstantes eran parte de Java 1.0 ... y probablemente mucho más atrás. Cuando quedó claro que el diseño tenía problemas, ya era demasiado tarde para solucionarlo. Lo mejor que podían hacer era agregar los System.setIn,setOut,setErrmétodos en Java 1.1 y luego tratar los problemas de especificación del lenguaje 1 .

Esto es similar a la cuestión de por qué hay un System.arraycopymétodo estático cuyo nombre viola las convenciones de nomenclatura de Java.


En cuanto a si esto es "mal diseño" o no, creo que lo es. Hay situaciones en las que el manejo actual sin OO es un problema grave. (Piense ... cómo puede ejecutar un programa Java dentro de otro cuando sus requisitos de flujo de "IO estándar" entran en conflicto. Piense ... en el código de prueba de la unidad que implica cambiar los flujos).

Sin embargo, también puedo relacionarme con el argumento de que la forma actual de hacer las cosas es más conveniente en muchos casos.


1 - Es interesante notar que las System.in,out,errvariables reciben una mención especial en el JLS por tener una "semántica especial". El JLS dice que si cambia el valor de un finalcampo, el comportamiento no está definido ... excepto en el caso de estos campos.

Stephen C
fuente
2

Creo que el objeto externo es inmutable, lo que lo hace de alguna manera seguro (esto es discutible) para mantenerse en un campo estático final público.

Muchas de las clases en el JDK no respetan los mejores principios de diseño orientado a objetos. Una razón para esto es el hecho de que fueron escritos hace casi 20 años, cuando la Orientación a Objetos solo estaba emergiendo como un paradigma convencional y muchos programadores simplemente no estaban familiarizados con ellos como lo están ahora. Un muy buen ejemplo de diseño de API incorrecto es la API de fecha y hora, que tardó 19 años en cambiar ...

m3th0dman
fuente
3
Haría la declaración aún más fuerte, una variable final pública (estática o no) es exactamente tan "segura" como un getter y preferiría la inmutibilidad sobre un par setter / getter. Los setters son casi siempre una mala idea (inmutable es bueno, y si no puede ser inmutable, el método que "establece" probablemente también merezca un poco de lógica de negocios), si necesita un getter también podría hacerlo público final, pero no es preferible hacer nada: no le pidas a una clase sus datos, pídele a una clase que haga algo con sus datos.
Bill K
@ Bill: Sí, también conocida como la Ley de Demeter (aunque no es una ley, sino una guía).
sleske
2

Esta respuesta es genial y verdadera.

Quería agregar que en algunos casos se hicieron compromisos por el bien de la usabilidad.

Los objetos de tipo String se pueden instanciar sin un nuevo evento cuando String no es primitivo:

String s = "Hello";

Al ser una cadena no primitiva, se debe instanciar así:

String s = new String("Hello");          // this also works 

Pero el compilador permite la opción más corta y menos OO, porque String es, con mucho, la clase más utilizada en la API.

También las matrices se pueden inicializar de una manera no OO:

int i[] = {1,2,3};

Por extraño que parezca, un objeto es una instancia de una clase o una matriz . Las matrices de significado son un tipo de clase completamente separado.

Las matrices tienen un lengthcampo público que no es una constante. Además, no hay documentación sobre las matrices de clases de las que se trata. (no confundir con la clase Arrays o java.reflect.Array).

int a = myArray.length;    // not length()
Tulains Córdova
fuente
66
Hay cosas diferentes: new String("Hello")siempre creará un nuevo objeto String. Mientras String s = "Hello";usará el objeto interno. Comparar: "Hello" == "Hello"puede ser cierto, mientras new String("Hello") == new String("Hello")que siempre es falso. Hay una magia de optimización de tiempo de compilación sucediendo en el primero que no es posible con new String("Hello"). Ver en.wikipedia.org/wiki/String_interning
@MichaelT Agregué algo de extrañeza extra en los arreglos, solo por el bien de la integridad.
Tulains Córdova
2
@Tarik Me opuse a que la "Cadena no sea primitiva, debería ser instanciada así: String s = new String("Hello");" lo cual es incorrecto. La opción más corta ( String s = "Hello";) es más correcta debido al internamiento de cadenas.
2
Con String, no hay una opción "más correcta". Ambos son correctos. Sin embargo, como dice MichaelT, se prefiere el más corto debido al internamiento de String.
Andres F.
1
@AndresF. Cambié "menos correcto" por "menos OO".
Tulains Córdova