Penser en Java 2nde édition | - | Sommaire | Préface | Avant-propos | Chapitre : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Annexe : A B C D | Tables des matières | - | Thinking in Java |
Le contrôle d'accès est souvent appelé cacher l'implémentation [implementation hiding] . L'enveloppement [wrapping] des données et méthodes à l'intérieur des classes combiné au masquage de l'implémentation est souvent appelé encapsulation [34]. Le résultat est un type de données avec des caractéristiques et des comportements.
Le contrôle d'accès pose des limites sur un type de données pour deux raisons importantes. La première est de déterminer ce que les programmeurs clients peuvent et ne peuvent pas utiliser. On peut construire les mécanismes internes dans la structure sans se préoccuper du risque que les programmeurs clients prennent ces mécanismes internes comme faisant partie de l'interface qu'ils doivent utiliser.
Ceci amène directement à la deuxième raison, qui est de séparer l'interface de son implémentation. Si la structure est utilisée dans un ensemble de programmes, mais que les programmeurs clients ne peuvent qu'envoyer des messages à l'interface public, alors on peut modifier tout ce qui n'est pas public (c'est à dire « amical », protected, ou private) sans que cela nécessite des modifications du code client.
Nous sommes maintenant dans le monde de la programmation orientée objet, dans lequel une class est en fait la description d' « une classe d'objets », comme on décrirait une classe des poissons ou une classe des oiseaux. Tout objet appartenant à cette classe partage ces caractéristiques et comportements. La classe est une description de la façon dont les objets de ce type vont nous apparaître et se comporter.
Dans le langage de POO d'origine, Simula-67 , le mot-clé class était utilisé pour décrire un nouveau type de données. Le même mot-clé a été repris dans la plupart des langages orientés objet. Ceci est le point central de tout le langage : la création de nouveaux types de données qui sont plus que simplement des boîtes contenant des données et des méthodes.
La classe est le concept de POO fondamental en Java. C'est l'un des mots-clés qui ne sera pas mis en gras dans ce livre, ça devient lourd pour un mot aussi souvent répété que « class ».
Pour plus de clarté, il est préférable d'utiliser un style de création des classes qui place les membres public au début, suivi par les membres protected, amicaux et private. L'avantage de ceci est que l'utilisateur de la classe peut voir ce qui est important pour lui (les membres public, parce qu'on peut y accéder de l'extérieur du fichier), en lisant depuis le début et en s'arrêtant lorsqu'il rencontre les membres non-public, qui font partie de l'implémentation interne :
Ceci ne la rendra que partiellement plus lisible parce que l'interface et l'implémentation sont encore mélangés. C'est-à-dire qu'on voit toujours le code source (l'implémentation) parce qu'il est là dans la classe. Cependant, la documentation sous forme de commentaires supportée par javadoc (décrite au Chapitre 2) diminue l'importance de la lisibilité du code par le programmeur client. Afficher l'interface au consommateur d'une classe est normalement le travail du class browser, un outil dont le travail consiste à inspecter toutes les classes disponibles et de montrer ce qu'on peut en faire (c'est à dire quels membres sont disponibles) de façon pratique. Au moment où vous lisez ceci, les browsers devraient faire partie de tout bon outil de développement Java.
En Java, les spécificateurs d'accès peuvent aussi être utilisés pour déterminer quelles classes d'une bibliothèque seront accessibles aux utilisateurs de cette bibliothèque. Si on désire qu'une classe soit disponible pour un programmeur client, on place le mot-clé public quelque part devant l'accolade ouvrante du corps de la classe. Ceci permet de contrôler le fait même qu'un programmeur client puisse créer un objet de cette classe.
Pour contrôler l'accès à la classe, le spécificateur doit apparaître avant le mot-clé class. Donc on peut dire :
Maintenant, si le nom de la bibliothèque est mylib, tout programmeur client peut accéder à Widget en disant
ou
Il y a cependant un ensemble de contraintes supplémentaires :
Que se passe-t-il si on a une classe dans mylib qu'on utilise uniquement pour accomplir les tâches effectuées par Widget ou une autre classe public de mylib ? On ne veut pas créer de documentation pour un programmeur client, et on pense que peut-être plus tard on modifiera tout et qu'on refera toute la classe en lui en substituant une nouvelle. Pour garder cette possibilité, il faut s'assurer qu'aucun programmeur client ne devienne dépendant des détails d'implémentation cachés dans mylib. Pour réaliser ceci il suffit d'enlever le mot-clé public de la classe, qui devient dans ce cas amicale. (Cette classe ne peut être utilisée que dans ce package.)
Remarquez qu'une classe ne peut pas être private (cela ne la rendrait accessible à personne d'autre que cette classe), ou protected [35]. Il n'y a donc que deux choix pour l'accès aux classes : « amical » ou public. Si on ne veut pas que quelqu'un d'autre accède à cette classe, on peut rendre tous les constructeurs private, ce qui empêche tout le monde de créer un objet de cette classe, à part soi-même dans un membre static de la classe [36]. Voici un exemple :
Jusqu'ici, la plupart des méthodes retournaient soit void soit un type primitif, ce qui fait que la définition :
pourrait paraître un peu confuse. Le mot devant le nom de la méthode (access) dit ce que retourne la méthode. Jusqu'ici cela a été le plus souvent void, ce qui signifie qu'elle ne retourne rien. Mais on peut aussi retourner la référence à un objet, comme c'est le cas ici. Cette méthode retourne une référence à un objet de la classe Soup.
La classe Soup montre comment empêcher la création directe d'une classe en rendant tous les constructeurs private. Souvenez-vous que si vous ne créez pas explicitement au moins un constructeur, le constructeur par défaut (un constructeur sans arguments) sera créé pour vous. En écrivant ce constructeur par défaut, il ne sera pas créé automatiquement. En le rendant private, personne ne pourra créer un objet de cette classe. Mais alors comment utilise-t-on cette classe ? L'exemple ci-dessus montre deux possibilités. Premièrement, une méthode static est créée, elle créee un nouveau Soup et en retourne la référence. Ceci peut être utile si on veut faire des opérations supplémentaires sur Soup avant de le retourner, ou si on veut garder un compteur du nombre d'objets Soup créés (peut-être pour restreindre leur population).
La seconde possibilité utilise ce qu'on appelle un patron de conception [design pattern], qui est expliqué dans Thinking in Patterns with Java, téléchargeable sur www.BruceEckel.com. Ce patron particulier est appelé un « singleton » parce qu'il n'autorise la création que d'un seul objet. L'objet de classe Soup est créé comme un membre static private de Soup, ce qui fait qu'il n'y en a qu'un seul, et on ne peut y accéder qu'à travers la méthode public access().
Comme mentionné précédemment, si on ne met pas de spécificateur d'accès il est « amical » par défaut. Ceci signifie qu'un objet de cette classe peut être créé par toute autre classe du package, mais pas en dehors du package (souvenez-vous que tous les fichiers dans le même répertoire qui n'ont pas de déclaration package explicite font implicitement partie du package par défaut pour ce répertoire). Cependant, si un membre static de cette classe est public, le programmeur client peut encore accéder à ce membre static même s'il ne peut pas créer un objet de cette classe.
Dans toute relation il est important d'avoir des limites respectées par toutes les parties concernées. Lorsqu'on crée une bibliothèque, on établit une relation avec l'utilisateur de cette bibliothèque (le programmeur client) qui est un autre programmeur, mais qui construit une application ou qui utilise la bibliothèque pour créer une bibliothèque plus grande.
Sans règles, les programmeurs clients peuvent faire tout ce qu'ils veulent des membres de la classe, même si vous préféreriez qu'ils ne manipulent pas directement certains des membres. Tout est à découvert dans le monde entier.
Ce chapitre a décrit comment les classes sont construites pour former des bibliothèques ; d'abord, comment un groupe de classes est empaqueté [packaged]dans une bibliothèque, et ensuite comment la classe contrôle l'accès à ses membres.
On estime qu'un projet programmé en C commence à s'écrouler losqu'il atteint 50K à 100K de lignes de code, parce que le C a un seul « espace de nommage », et donc les noms commencent à entrer en conflit, provoquant un surcroît de gestion. En Java, le mot-clé package, le principe de nommage des packages et le mot-clé import donnent un contrôle complet sur les noms, et le problème de conflit de nommage est facilement évité.
Il y a deux raisons pour contrôler l'accès aux membres. La première est d'écarter les utilisateurs des outils qu'ils ne doivent pas utiliser ; outils qui sont nécessaires pour les traitements internes des types de données, mais qui ne font pas partie de l'interface dont les utilisateurs ont besoin pour résoudre leurs problèmes particuliers. Donc créer des méthodes et des champs private est un service rendu aux utilisateurs parce qu'ils peuvent facilement voir ce qui est important pour eux et ce qu'ils peuvent ignorer. Cela leur simplifie la compréhension de la classe.
La deuxième raison, et la plus importante, pour le contrôle d'accès, est de permettre au concepteur de bibliothèque de modifier les fonctionnements internes de la classe sans se soucier de la façon dont cela peut affecter le programmeur client. On peut construire une classe d'une façon, et ensuite découvrir qu'une restructuration du code améliorera grandement sa vitesse. Si l'interface et l'implémentation sont clairement séparées et protégées, on peut y arriver sans forcer l'utilisateur à réécrire son code.
Les spécificateurs d'accès en Java donnent un contrôle précieux au créateur d'une class. Les utilisateurs de la class peuvent voir clairement et exactement ce qu'ils peuvent utiliser et ce qu'ils peuvent ignorer. Encore plus important, on a la possiblité de garantir qu'aucun utilisateur ne deviendra dépendant d'une quelconque partie de l'implémentation d'une class. Sachant cela en tant que créateur d'une class, on peut en modifier l'implémentation en sachant qu'aucun programmeur client ne sera affecté par les modifications car il ne peut accéder à cette partie de la class.
Lorsqu'on a la possibilité de modifier l'implémentation, on peut non seulement améliorer la conception plus tard, mais on a aussi le droit de faire des erreurs . Quelles que soient les soins que vous apportez à votre planning et à votre conception, vous ferez des erreurs. Savoir qu'il est relativement peu dangereux de faire ces erreurs veut dire que vous ferez plus d'expériences, vous apprendrez plus vite, et vous finirez plus vite votre projet.
L'interface publique d'une classe est ce que l'utilisateur voit, donc c'est la partie qui doit être « correcte » lors de l'analyse et la conception. Et même ici vous avez encore quelques possibilités de modifications. Si vous n'avez pas la bonne interface du premier coup, vous pouvez ajouter des méthodes , pour autant que vous ne supprimiez pas celles que les programmeurs clients ont déjà utilisé dans leur code.
Les solutions des exercices sélectionnés sont disponibles dans le document électronique The Thinking in Java Annotated Solution Guide, disponible à un prix raisonnable sur www.BruceEckel.com.
Créez enuite le fichier suivant dans un répertoire autre que c05 :
Expliquez pourquoi le compilateur génère une erreur. Le fait de mettre la classe Foreign dans le package c05.local changerait-il quelque chose ?
[32] Rien en Java n'oblige à utiliser un interpréteur. Il existe des compilateurs Java de code natif qui génèrent un seul fichier exécutable.
[33] Il y a un autre effet dans ce cas. Comme le constructeur par défaut est le seul défini, et qu'il est private, il empêchera l'héritage de cette classe. (Un sujet qui sera présenté dans le Chapitre 6.)
[34] Cependant, on parle souvent aussi d'encapsulation pour le seul fait de cacher l'implémentation.
[35] En fait, une classe interne[inner class] peut être private ou protected, mais il s'agit d'un cas particulier. Ceux-ci seront présentés au Chapitre 7.
[36] On peut aussi le faire en héritant (Chapitre 6) de cette classe.