web-dev-qa-db-fra.com

Pourquoi Java a-t-il rendu l'accès au package par défaut?

Je pose cette question parce que je pense qu'ils l'ont fait pour une très bonne raison et que la plupart des gens ne l'utilisent pas correctement, d'après mon expérience dans l'industrie jusqu'à présent de toute façon. Mais si ma théorie est vraie, je ne sais pas pourquoi ils ont inclus le modificateur d'accès privé ...?

Je crois que si l'accès par défaut est correctement utilisé, il offre une testabilité améliorée tout en maintenant l'encapsulation. Et il rend également le modificateur d'accès privé redondant.

Le modificateur d'accès par défaut peut être utilisé pour fournir le même effet en utilisant un package unique pour les méthodes qui doivent être cachées du reste du monde, et il le fait sans compromettre la testabilité, car les packages dans un dossier de test, avec le même sont capable d'accéder à toutes les méthodes par défaut déclarées dans un dossier source.

Je crois que c'est pourquoi Java utilise l'accès au package comme "par défaut". Mais je ne sais pas pourquoi ils incluaient également un accès privé, je suis sûr qu'il existe un cas d'utilisation valide ...

26
newlogic

Je suppose qu'ils avaient une bonne idée de ce qu'un programmeur moyen ferait. Et par programmeur moyen, je veux dire celui qui n'est pas vraiment bon en programmation, mais qui réussit quand même car il n'y en a pas assez et ils sont chers.

S'ils rendaient l'accès "public" par défaut, la plupart des programmeurs ne prendraient jamais la peine d'utiliser autre chose. Le résultat serait beaucoup de code spaghetti partout. (Parce que si vous êtes techniquement autorisé à appeler n'importe quoi de n'importe où, pourquoi vous embêter à encapsuler une logique dans une méthode?)

Rendre "privé" l'accès par défaut ne serait que légèrement meilleur. La plupart des programmeurs débutants inventeraient simplement (et partageraient sur leurs blogs) une règle empirique selon laquelle "vous devez écrire" public "partout", puis ils se plaindraient pourquoi Java est si mauvais qu'il les oblige à écrire "public" partout. Et ils produiraient aussi beaucoup de code spaghetti.

L'accès aux packages est quelque chose qui permet aux programmeurs d'utiliser leurs techniques de programmation bâclées lors de l'écriture de code dans un package, mais ils doivent ensuite le reconsidérer lors de la création de plusieurs packages. Il s'agit d'un compromis entre les bonnes pratiques commerciales et la réalité laide. Comme: écrire du code spaghetti, si vous insistez, mais veuillez laisser le désordre laid dans le paquet; au moins créer des interfaces plus agréables parmi les packages.

Il y a probablement d'autres raisons aussi, mais je ne sous-estimerais pas celle-ci.

25
Viliam Búr

L'accès par défaut ne rend pas le modificateur d'accès privé redondant.

La position des concepteurs de langage sur cela se reflète dans le tutoriel officiel - Contrôle de l'accès aux membres d'une classe et c'est assez clair (pour votre commodité, déclaration pertinente dans la citation faite bold ):

Conseils sur le choix d'un niveau d'accès:

Si d'autres programmeurs utilisent votre classe, vous voulez vous assurer que les erreurs de mauvaise utilisation ne peuvent pas se produire. Les niveaux d'accès peuvent vous y aider.

  • Utilisez le niveau d'accès le plus restrictif qui a du sens pour un membre particulier. Utilisez privé sauf si vous avez une bonne raison de ne pas le faire.
  • Évitez les champs publics à l'exception des constantes. (De nombreux exemples du didacticiel utilisent des champs publics. Cela peut aider à illustrer certains points de manière concise, mais n'est pas recommandé pour le code de production.) Les champs publics ont tendance à vous lier à une implémentation particulière et à limiter votre flexibilité dans la modification de votre code.

Votre appel à la testabilité comme justification de complètement la suppression du modificateur privé est incorrect, comme en témoignent par exemple les réponses dans Nouveau sur TDD. Dois-je éviter les méthodes privées maintenant?

Bien sûr, vous pouvez avoir des méthodes privées, et bien sûr, vous pouvez les tester.

Soit il y a certains moyen de faire fonctionner la méthode privée, auquel cas vous pouvez la tester de cette façon, soit il y a non moyen d'obtenir le privé à exécuter, dans ce cas: pourquoi diable essayez-vous de le tester, supprimez simplement la fichue chose ...


La position des concepteurs de langage sur le but et l'utilisation de l'accès au niveau du package est expliquée dans un autre tutoriel officiel, Création et utilisation de packages et il n'a rien en commun avec l'idée de supprimer des modificateurs privés (pour votre commodité, pertinent déclaration dans la citation faite en gras ):

Vous devez regrouper ces classes et l'interface dans un package pour plusieurs raisons, notamment les suivantes:

  • Vous et d'autres programmeurs pouvez facilement déterminer que ces types sont liés ...
  • Vous pouvez autoriser les types au sein du package à avoir un accès illimité les uns aux autres tout en limitant l'accès aux types en dehors du package ...

<rant "Je pense que j'ai entendu assez de gémissements. Je suppose qu'il est temps de dire haut et fort ...">

Les méthodes privées sont avantageuses pour les tests unitaires.

La note ci-dessous suppose que vous connaissez couverture du code . Sinon, prenez le temps d'apprendre, car il est très utile pour ceux qui s'intéressent aux tests unitaires et aux tests.

Très bien, j'ai donc cette méthode privée et des tests unitaires, et une analyse de couverture me disant qu'il y a un écart, ma méthode privée n'est pas couverte par les tests. Maintenant...

Qu'est-ce que je gagne à le garder privé

Comme la méthode est privée, la seule façon de procéder est d'étudier le code pour savoir comment il est utilisé via une API non privée. En règle générale, une telle étude révèle que la raison de l'écart est que le scénario d'utilisation particulier manque dans les tests.

    void nonPrivateMethod(boolean condition) {
        if (condition) {
            privateMethod();
        }
        // other code...
    }

    // unit tests don't invoke nonPrivateMethod(true)
    //   => privateMethod isn't covered.

Dans un souci d'exhaustivité, d'autres raisons (moins fréquentes) à de tels écarts de couverture pourraient être des bogues dans la spécification/conception. Je ne vais pas m'y plonger profondément ici, pour garder les choses simples; il suffit de dire que si vous affaiblissez la limitation d'accès "juste pour rendre la méthode testable", vous manquerez une chance d'apprendre que ces bogues existent.

Très bien, pour corriger l'écart, j'ajoute un test unitaire pour le scénario manquant, répète l'analyse de couverture et vérifie que l'écart est disparu. Qu'est-ce que j'ai maintenant? J'ai comme nouveau test unitaire pour une utilisation spécifique de l'API non privée.

  1. Un nouveau test garantit que le comportement attendu pour cette utilisation ne changera pas sans préavis car s'il change, le test échouera.

  2. Un lecteur extérieur peut se pencher sur ce test et apprendre comment il est censé utiliser et se comporter (ici, le lecteur extérieur comprend mon futur moi-même, car j'ai tendance à oublier le code un mois ou deux après mon fait avec elle).

  3. Un nouveau test est tolérant au refactoring (dois-je refactoriser les méthodes privées? Vous pariez!) Quoi que je fasse pour privateMethod, je veux toujours tester nonPrivateMethod(true). Peu importe ce que je fais pour privateMethod, il ne sera pas nécessaire de modifier le test car la méthode n'est pas directement invoquée.

Pas mal? Tu paries.

Qu'est-ce que je perds de l'affaiblissement de la limitation d'accès

Imaginez maintenant qu'au lieu de ci-dessus, j'affaiblisse simplement la limitation d'accès. Je saute l'étude du code qui utilise la méthode et je continue directement avec le test qui appelle mon exPrivateMethod. Génial? Ne pas!

  1. Dois-je passer un test pour une utilisation spécifique des API non privées mentionnées ci-dessus? Non: il n'y avait pas de test pour nonPrivateMethod(true) auparavant, et il n'y en a plus actuellement.

  2. Les lecteurs externes ont-ils une chance de mieux comprendre l'utilisation de la classe? Non. "- Hé, quel est le but de la méthode testée ici? - Oubliez ça, c'est strictement pour un usage interne. - Oups."

  3. Est-il tolérant au refactoring? Pas question: quoi que je change dans exPrivateMethod, cela cassera probablement le test. Renommer, fusionner dans une autre méthode, changer les arguments et tester arrêtera simplement la compilation. Mal de crâne? Tu paries!

En résumé , s'en tenir à une méthode privée m'apporte une amélioration utile et fiable dans les tests unitaires. En revanche, l'affaiblissement des limitations d'accès "pour la testabilité" ne me donne qu'un morceau de code de test obscur et difficile à comprendre, qui risque en outre d'être définitivement brisé par une refactorisation mineure; franchement ce que j'obtiens ressemble étrangement à dette technique .

</rant>

14
gnat

La raison la plus probable est: cela a à voir avec l'histoire. L'ancêtre de Java, Oak, n'avait que trois niveaux d'accès: privé, protégé, public.

Sauf que private dans Oak était l'équivalent du package private dans Java. Vous pouvez lire la section 4.10 de Oak's Language Specification (c'est moi qui souligne):

Par défaut, toutes les variables et méthodes d'une classe (y compris les constructeurs) sont privées. Les variables et méthodes privées ne sont accessibles que par les méthodes déclarées dans la classe, et non par ses sous-classes ou toute autre classe ( sauf pour les classes du même package).

Donc à votre point, l'accès privé, comme connu en Java, n'était pas là à l'origine. Mais lorsque vous avez plus de quelques classes dans un package, ne pas avoir privé comme nous le savons maintenant conduirait à un cauchemar de collision de noms (par exemple, Java.until.concurrent a près de 60 classes), c'est probablement pourquoi ils l'a présenté.

Cependant, la sémantique d'accès au package par défaut (initialement appelée privée) n'a pas été modifiée entre Oak et Java.

9
assylias

Je crois que si l'accès par défaut est correctement utilisé, il offre une testabilité améliorée tout en maintenant l'encapsulation. Et il rend également le modificateur d'accès privé redondant.

Il y a des situations où l'on voudrait deux classes distinctes qui sont plus étroitement liées que de tout exposer au public. Cela a des idées similaires d '"amis" en C++ où une autre classe qui est déclarée "amie" d'une autre peut accéder à ses membres privés.

Si vous regardez dans les entrailles de classes telles que BigInteger , vous trouverez un certain nombre de protections par défaut des packages sur les champs et les méthodes (tout ce qui est un triangle bleu dans la liste). Cela permet aux autres classes du package Java.math d'avoir un accès plus optimal à leurs entrailles pour des opérations spécifiques (vous pouvez trouver ces méthodes appelées BigDecimal - BigDecimal est soutenu par un BigInteger et enregistre la réimplémentation de BigInteger à nouveau . Que ce soit au niveau du package n'est pas un problème de protection car Java.math est un package scellé et aucune autre classe ne peut y être ajoutée.

D'un autre côté, il y a beaucoup de choses qui sont en effet privées, comme elles devraient l'être. La plupart des colis ne sont pas scellés. Sans privé, votre collègue pourrait mettre une autre classe dans ce package et accéder au champ (plus) privé et rompre l'encapsulation.

Il convient de noter que les tests unitaires n'étaient pas une chose à laquelle on pensait à l'époque de la construction des étendues de protection. JUnit (le framework de test XUnit pour Java) n'a été conçu qu'en 1997 (un récit de cette histoire peut être lu sur http://www.martinfowler.com/bliki/Xunit.html ). Les versions Alpha et Beta du JDK étaient en 1995 et JDK 1.0 en 1996, bien que les niveaux de protection n'aient pas vraiment été cloués avant JDK 1.0.2 (avant cela, vous pouviez avoir un private protected Niveau de protection).

Certains diront que la valeur par défaut ne devrait pas être au niveau du package, mais privée. D'autres soutiennent qu'il ne devrait pas y avoir de protection par défaut mais tout devrait être explicitement indiqué - certains adeptes de cela écriront du code tel que:

public class Foo {
    /* package */ int bar = 42;
    ...
}

Notez le commentaire ici.

La vraie raison pour laquelle la protection au niveau du package est la valeur par défaut est probablement perdue dans les notes de conception de Java (J'ai creusé et je n'en trouve pas pourquoi articles, beaucoup de gens expliquent la différence, mais aucun ne dit "c'est la raison").

Je vais donc deviner. Et c'est tout ce que c'est - une supposition.

Tout d'abord, les gens essayaient toujours de comprendre la conception du langage. Depuis lors, les langues ont appris des erreurs et des succès de Java. Cela pourrait être répertorié comme une erreur de ne pas avoir quelque chose qui doit être défini pour tous les champs .

  • Les concepteurs ont vu un besoin occasionnel d'une relation "amie" de C++.
  • Les concepteurs souhaitaient des déclarations explicites pour public et private car celles-ci ont le plus d'impact sur l'accès.

Ainsi, privé n'est pas par défaut et bien, tout le reste s'est mis en place. La portée par défaut a été laissée par défaut. Il peut avoir été le premier champ d'application créé et dans l'intérêt de rétrocompatibilité rigoureuse avec le code plus ancien, a été laissé de cette façon.

Comme je l'ai dit, ce sont tous des suppositions .

5
user40980

L'encapsulation est une façon de dire "vous n'avez pas à y penser".

                 Access Levels
Modifier    Class   Package  Subclass   World
public        Y        Y        Y        Y
protected     Y        Y        Y        N
no modifier   Y        Y        N        N
private       Y        N        N        N

Mettre ces termes en termes non techniques.

  1. Public: tout le monde doit y penser.
  2. Protégé: par défaut et les sous-classes doivent le savoir.
  3. Par défaut, si vous travaillez sur le paquet, vous le saurez (il est juste là devant vos yeux) pourrait tout aussi bien vous laisser en profiter.
  4. Privé, vous n'avez besoin de le savoir que si vous travaillez sur ce cours.
2
jmoreno

Je ne pense pas qu'ils se soient concentrés sur les tests, simplement parce que les tests n'étaient pas très courants à l'époque.

Ce que je pense qu'ils voulaient réaliser, c'était l'encapsulation au niveau du package. Une classe peut, comme vous le savez, avoir des méthodes et des champs internes et n'en exposer qu'une partie par le biais de membres publics. De la même manière, un package peut avoir des classes, des méthodes et des champs internes et n'en exposer qu'une partie. Si vous pensez de cette façon, un package a une implémentation interne dans un ensemble de classes, méthodes et champs, ainsi qu'une interface publique dans un autre ensemble (éventuellement se chevauchant partiellement) de classes, méthodes et champs.

Les codeurs les plus expérimentés que je connais pensent de cette façon. Ils prennent l'encapsulation et l'appliquent à un niveau d'abstraction supérieur à la classe: un package ou un composant si vous le souhaitez. Il me semble raisonnable que les concepteurs de Java étaient déjà aussi matures dans leurs conceptions, compte tenu de la capacité de Java à tenir le coup).

0
Alexander Torstling