Pourquoi Java n'utilise pas l'encapsulation avec certaines classes?

25

Ma question est liée aux classes System.inet System.out(il pourrait y en avoir d'autres comme celles de la bibliothèque Standard). Pourquoi donc? N'est-ce pas une mauvaise pratique en POO? Ne devrait-il pas être utilisé comme: System.getIn()et System.getOut()? J'ai toujours eu cette question et j'espère pouvoir trouver une bonne réponse ici.

Clawdidr
la source

Réponses:

36

La définition des champs inet outdans la Systemclasse est:

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

Ce sont des constantes. Ce sont aussi des objets, mais ce sont des constantes. C'est très similaire à la classe Math:

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

Ou dans la classe booléenne:

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

Ou dans la classe Couleur:

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);

Lorsque vous accédez à une constante publique qui ne change pas, il n'y a pas d'avantage significatif à l'encapsuler - conceptuellement ou en fonction des performances. C'est là. Ça ne va pas changer.

Il n'y a pas de réelle différence entre Color.whiteet System.out.


la source
Eh bien, je ne l'ai pas vu de cette façon, ce sont des objets constants. Voilà une assez bonne explication.
Clawdidr du
1
@Clawdidr dans le Java d'aujourd'hui, on pourrait envisager d'utiliser le enumpour les conserver ... bien qu'il enumsoit nouveau avec Java 1.5 (pas une option dans les 1.0 jours).
Juste par curiosité (pas moi-même développeur Java): y a-t-il une différence entre final staticet static final? Si oui, c'est quoi?
Marjan Venema
1
@MarjanVenema aucune différence, juste l'ordre préféré des modificateurs. La vérification du modificateur checkstyle montre l'ordre préféré. Apparemment, celui qui a écrit ces deux fichiers source dans la version jdk, je ne suis pas d'accord sur la commande. C'est simplement une convention.
:) et merci Michael, d'avoir pris le temps de répondre et pour le lien.
Marjan Venema
5

La vraie raison est qu'il s'agit d'un problème hérité. Les System.in,out,errconstantes faisaient partie de Java 1.0 ... et probablement beaucoup plus loin. Au moment où il était clair que la conception avait des problèmes, il était trop tard pour y remédier. Le mieux qu'ils pouvaient faire était d'ajouter les System.setIn,setOut,setErrméthodes dans Java 1.1, puis de traiter les problèmes de spécification du langage 1 .

Ceci est similaire à la question de savoir pourquoi il existe une System.arraycopyméthode statique dont le nom viole les conventions de dénomination Java.


Quant à savoir si c'est une "mauvaise conception" ou non, je pense que oui. Il y a des situations où la gestion non OO actuelle est un problème sérieux. (Pensez ... comment pouvez-vous exécuter un programme Java à l' intérieur d' un autre lorsque leurs exigences de flux "IO standard" entrent en conflit. Pensez ... au code de test unitaire qui implique de changer les flux.)

Cependant, je peux également comprendre l'argument selon lequel la manière actuelle de faire les choses est plus pratique dans de nombreux cas.


1 - Il est intéressant de noter que les System.in,out,errvariables reçoivent une mention spéciale dans le JLS comme ayant une "sémantique spéciale". Le JLS indique que si vous modifiez la valeur d'un finalchamp, le comportement n'est pas défini ... sauf dans le cas de ces champs.

Stephen C
la source
2

Je crois que l'objet out est immuable, ce qui le rend en quelque sorte sûr (c'est discutable) pour être gardé dans un champ statique final public.

De nombreuses classes du JDK ne respectent pas les meilleurs principes de conception orientée objet. L'une des raisons à cela est le fait qu'ils ont été écrits il y a près de 20 ans, lorsque l'Orientation-Orientation émergeait seulement comme un paradigme dominant et que de nombreux programmeurs ne les connaissaient tout simplement pas comme ils le sont maintenant. Un très bon exemple de mauvaise conception d'API est l'API Date & Time, qui leur a pris 19 ans pour changer ...

m3th0dman
la source
3
Je ferais la déclaration encore plus fort, une variable finale publique (statique ou non) est exactement aussi «sûre» qu'un getter et je préfère l'immuabilité sur une paire setter / getter. Les setters sont à peu près toujours une mauvaise idée (immuable est bon - et s'il ne peut pas être immuable la méthode qui "définit", il mérite probablement aussi une petite logique commerciale), si vous avez besoin d'un getter, vous pourriez aussi bien le rendre public final, mais ne faire ni l'un ni l'autre est préférable - ne demandez pas à une classe ses données, demandez à une classe de faire quelque chose avec ses données.
Bill K
@BillK: Oui, également connue sous le nom de loi de Déméter (bien que ce ne soit pas une loi, plutôt une directive).
sleske
2

Cette réponse est grande et vraie.

Je voulais ajouter que dans certains cas, des compromis ont été faits pour des raisons de convivialité.

Les objets de type String peuvent être instanciés sans nouvel événement, lorsque String n'est pas une primitive:

String s = "Hello";

La chaîne étant non primitive, elle doit être instanciée comme ceci:

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

Mais le compilateur permet l'option plus courte et moins OO, car String est de loin la classe la plus utilisée dans l'API.

Les tableaux peuvent également être initialisés de manière non OO:

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

Assez étrange, un objet est soit une instance d'une classe ou un tableau . Les tableaux de signification sont un type de classe complètement séparé.

Les tableaux ont un lengthchamp public qui n'est pas une constante. Il n'y a pas non plus de documentation sur les tableaux de classes dont est une instance. (à ne pas confondre avec la classe Arrays ou java.reflect.Array).

int a = myArray.length;    // not length()
Tulains Córdova
la source
6
Ce sont des choses différentes - new String("Hello")créera toujours un nouvel objet String. While String s = "Hello";utilisera l'objet interné. Comparer: "Hello" == "Hello"peut être vrai, alors qu'il new String("Hello") == new String("Hello")est toujours faux. Il y a une magie d'optimisation du temps de compilation qui se produit dans le premier qui n'est pas possible avec new String("Hello"). Voir en.wikipedia.org/wiki/String_interning
@MichaelT J'ai ajouté un peu de bizarrerie supplémentaire sur les tableaux, juste pour être complet.
Tulains Córdova
2
@Tarik Je m'opposais à ce que "la chaîne ne soit pas primitive, devrait être instanciée comme ceci: String s = new String("Hello");" ce qui est incorrect. L'option plus courte ( String s = "Hello";) est plus correcte en raison de l'internement de chaînes.
2
Avec String, il n'y a pas d'option "plus correcte". Les deux sont corrects. Cependant, comme le dit MichaelT, le plus court est préféré en raison de l'internement de chaînes.
Andres F.
1
@AndresF. J'ai changé "moins correct" par "moins d'OO".
Tulains Córdova