web-dev-qa-db-fra.com

Les modificateurs d'accès sont-ils importants?

La théorie est que les modificateurs d'accès améliorent la sécurité du code car ils prennent en charge l'encapsulation de l'état interne. Lorsque je fais de la POO, chaque langue que j'ai utilisée implémente une sorte de restriction d'accès. J'aime certains modèles d'accès mieux que d'autres.

Je fais partie d'une équipe de développeurs Java. Nos projets passent du temps à réviser le code en tenant compte des modificateurs d'accès, de leur pertinence et de l'utilisation de choses comme @VisibleForTesting (a Java annotation) Nos projets passent aussi occasionnellement du temps à dé-finaliser ou à dé-privatiser quelque chose dans une bibliothèque tierce si un changement de code source n'est pas possible.

Je suis allé à la recherche de recherches qui montrent comment l'utilisation de modificateurs d'accès affecte la densité des défauts ou les occurrences d'erreurs d'exécution. Je ne trouve aucune étude à ce sujet. Peut-être que mon Google-Fu est faible. Quelles sont les preuves que les modificateurs d'accès fournissent réellement les avantages que nous supposons qu'ils font? Où sont les études qui quantifient les problèmes d'utilisation des modificateurs d'accès?

39
ahoffer

Pour compléter les excellentes réponses existantes de Doc Brown et de Gort the Robot , une autre façon d'examiner les restrictions d'accès est l'une des nombreuses façons dont nous écrivons programmes modulaires .

Dans l'architecture la plus simple, toutes les variables et lignes de code sont accessibles depuis toutes les étendues. Cela vous permettrait d'assembler le code dans n'importe quelle combinaison que vous vouliez, et à mesure que le programme se développe, le nombre de combinaisons augmente de façon exponentielle, ce qui rend difficile de raisonner sur ce que le programme fera.

Pour améliorer cela, nous décomposons les programmes en modules réutilisables de différents types. Chaque module a un contrat définissant les façons dont le programme peut interagir avec lui; et une implémentation qui est cachée aux autres codes.

Le type de module le plus simple est une fonction: la signature de fonction (nom, paramètres et type de retour) définit le contrat; et l'implémentation peut inclure des variables locales et des lignes de code qui ne peuvent pas être sautées depuis l'extérieur de la fonction. Le nombre de combinaisons possibles se réduit alors de toutes les interactions possibles de variables et de lignes de code, à toutes les interactions possibles de fonctions.

Les modificateurs de visibilité répètent simplement le même avantage: en regroupant des fonctions et des variables dans des classes ou des packages, nous pouvons donner à chaque groupement un contrat et une implémentation. Le nombre de combinaisons possibles se réduit davantage, à des interactions valides de classes ou de packages.

Tout cela peut être implémenté par convention plutôt que dans le cadre du langage - vous pouvez nommer soigneusement les variables globales plutôt que d'avoir une portée locale, et vous pouvez nommer les fonctions avec soin plutôt que d'avoir une visibilité. Plus ces conventions sont universelles, plus les outils peuvent les soutenir - par exemple les vérificateurs hors ligne peuvent vous dire si vous validez des contrats, et les IDE ne peuvent suggérer que des membres "visibles".

Pour être vraiment universelle, la convention doit être intégrée dans le langage et appliquée par outil par défaut . Cela est particulièrement important lors du partage ou de la remise de code: si la plupart des outils lisent un trait de soulignement comme signifiant "privé de cette classe", mais le le compilateur/runtime par défaut n'applique pas cela, vous devez juger si le code de quelqu'un d'autre l'utilise "correctement". Si vous voyez private dans un programme Java, vous pouvez faire des hypothèses assez solides sur le contrat.

En fin de compte, il s'agit d'échanger la flexibilité pour la capacité de raisonner et de maintenir le code. Tout comme il existe des cas où l'utilisation de goto plutôt que de fonctions permet de résoudre un problème, il existe des cas où l'accès à une méthode privée permet de faire un travail. Si le langage n'a pas fait ce compromis par ce qu'il applique, le programmeur doit faire le même compromis par la façon dont ils l'utilisent.

11
IMSoP

Permettez-moi de vous donner un exemple concret du moment où les modificateurs d'accès "importaient" que j'ai rencontrés personnellement:

Notre logiciel est principalement python, et d'une manière qui python diffère de la plupart des autres langues OO est qu'il n'y a pas de modificateurs d'accès explicites. Au lieu de cela, il est conventionnel de méthodes et attributs de préfixe qui doivent être privés avec un trait de soulignement.

Un jour, un développeur travaillait sur une fonctionnalité particulière et ne pouvait pas la faire fonctionner avec l'interface de l'objet avec lequel il travaillait. Mais il a remarqué que s'il travaillait avec un attribut particulier marqué comme privé, il pouvait faire ce qu'il voulait faire. Il l'a donc fait, l'a archivé et (malheureusement) il a glissé après la révision du code et dans la branche principale.

Avance rapide de deux ans. Ce développeur avait évolué. Nous avons mis à jour une version plus récente d'une bibliothèque sous-jacente. Un code qui avait été fiable a soudainement cessé de fonctionner. Cela a entraîné beaucoup de débogage et des messages de va-et-vient avec une autre équipe dans un fuseau horaire différent.

Finalement, nous avons résolu le problème: les développeurs qui possédaient cet objet sous-jacent ont changé la façon dont il fonctionnait d'une manière très subtile. Suffisamment subtil pour qu'aucune exception ne soit levée, aucune autre erreur ne s'est produite. La bibliothèque est devenue floconneuse. Cela est arrivé parce que les développeurs de cette bibliothèque n'avaient aucune idée qu'ils faisaient quoi que ce soit qui causerait des problèmes à quiconque. Ils changeaient quelque chose interne, pas l'interface.

Donc, après le fait, nous avons fait ce qui aurait dû être fait à l'origine: nous avons demandé aux développeurs de la bibliothèque d'ajouter une méthode publique qui a résolu notre problème plutôt que de contourner les internes de leurs objets.

C'est ce que les modificateurs d'accès empêchent. Ils garantissent que la séparation de l'interface et de la mise en œuvre est claire. Il permet aux utilisateurs de savoir exactement ce qu'ils peuvent faire avec la classe en toute sécurité et aux développeurs de la classe de changer les internes sans casser le logiciel de l'utilisateur.

Vous pouvez faire tout cela avec la convention, pas la force, comme python montre, mais même là où c'est juste une convention, avoir cette séparation public/privé est une grande aubaine pour la maintenabilité.

68
Gort the Robot

D'après mon expérience, le principal avantage des modificateurs d'accès n'est pas le nombre d'erreurs qu'ils empêchent, il ne serait donc pas très logique d'essayer de faire des statistiques à ce sujet.

Le véritable avantage est, en particulier dans les grandes bases de code, qu'elles facilitent la analyse d'impact des modifications d'une base de code de plusieurs ordres de grandeur. Et c'est quelque chose que j'expérimente quotidiennement. Pour effectuer un changement n'affectant que des éléments privés, les conséquences peuvent généralement être réduites à une partie relativement petite de la base de code. Pour apporter des modifications aux parties publiques, l'analyse est souvent beaucoup plus difficile, car une modification d'une fonction publique dans un module pourrait théoriquement affecter tous les modules dépendants.

Bien sûr, même un changement à une fonction privée peut avoir des conséquences à plus grande échelle, et tous les changements apportés à une méthode publique n'affectent pas la base de code dans son ensemble. Mais les modificateurs d'accès nous permettent de mieux raisonner sur ce que et où la majorité des changements auront (ou n'auront pas) des effets voulus ou indésirables, et laissez-nous donc implémenter ces changements correctement de manière plus efficace.

Permettez-moi d'ajouter que d'après mon expérience, pour les petits programmes avec moins de, disons, 2K lignes de code, l'effet est petit, mais lorsque vous travaillez sur un système avec> 200k lignes de code, les choses se présentent différemment.

26
Doc Brown

Les modificateurs d'accès sont une technique pour implémenter encapsulation. Utilisez-les si vous recherchez les avantages de l'encapsulation.

En général, les meilleures pratiques en génie logiciel ne sont pas dictées par des études universitaires ou des expériences à double insu et évaluées par des pairs; ils sont dictés par le pragmatisme et l'expérience. La preuve est "ça marche dans la vraie vie?" Ou (étant pragmatique) "les avantages l'emportent-ils sur les coûts?"

Cela dépend également de vos objectifs. Si vous venez d'un arrière-plan où la brièveté est plus importante que l'explicitness, vous constaterez peut-être que les modificateurs d'accès ne vous profitent pas du tout. Mais parfois, vous n'avez pas ce choix. Lorsque vous travaillez avec une équipe de développeurs Java développeurs, les bonnes choses à faire sont généralement celles qui correspondent à la sensibilité de Java.

Quand à Rome ...

17
Robert Harvey

Mis à part la sécurité et la maintenabilité, il vous garde sain d'esprit.

Faisons ce monde réel, sans le jumbo fantaisie. Supposons que vous obteniez une bibliothèque de classe pour vous aider à faire votre travail. Vous savez ce qu'il est censé faire, vous n'avez tout simplement pas encore travaillé avec. Vous l'ajoutez donc à votre environnement de développement et regardez ce qu'il contient.

Hou la la! 1200 cours! À quoi ... à quoi ces gars pensaient-ils? J'ai seulement besoin de faire tout ce qui est raisonnable pour pouvoir me débrouiller avec 5, 10 cours. Et ils me donnent tout ça. Je parie que 99% de cela est totalement hors de propos pour moi, pourquoi ne me montrent-ils pas simplement les cours qui comptent vraiment pour moi?!

Vous parcourez donc les espaces de nom et repérez un nom de classe qui semble prometteur. Il s'appelle ThisIsTheOneYouActuallyWantToUse. Comme c'est pratique. Vous tapez:

ThisIsTheOneYouActuallyWantToUse instance = new ThisIsTheOneYouActuallyWantToUse();
instance.

et IntelliSense vous montre toutes les méthodes et propriétés que cette classe a à offrir. Hou la la! 200 membres! A quoi pensaient ces gars?! Ai-je besoin de tout cela? Je parie que non. Pourquoi ne m'ont-ils pas simplement montré ceux qui m'importaient en tant qu'utilisateur?

Etc. Vous vous plaignez pour le reste de la journée et vous rentrez chez vous fatigué et mécontent.

[Éditer]

Cela illustre l'expérience d'un développeur d'applications utilisant une bibliothèque tierce, essayant de trouver une méthode utile dans une classe appropriée. Notez que ce problème se joue à tous les niveaux. Cela peut être un problème lorsqu'un développeur d'une équipe examine le travail d'un autre. Pensez à un utilisateur final qui obtient un menu avec quelques centaines d'articles tous aplatis à un niveau. Ou un document sans chapitres ni paragraphes, seulement de la ponctuation. Une carte d'une ville avec un nom de rue (si vous utilisez un entier pour un numéro de maison, cela simplifierait radicalement les choses, qui a besoin de tous ces secteurs stupides et noms de rue de toute façon?).

En tant qu'utilisateur du travail des autres, vous chercherez toujours une hiérarchie, des morceaux principaux qui représentent quelque chose. Vous devez voir la forêt depuis les arbres pour comprendre quoi que ce soit et zoomer sur ce dont vous avez besoin en quelques étapes. Les modificateurs d'accès ne représentent qu'un niveau de la chaîne d'organisations, de systèmes, d'applications, de modules, d'espaces de noms, de classes et de membres.

8
Martin Maat

Je ne connais pas de recherches spécifiques, mais il y a une multitude de projets logiciels échoués à étudier. Certains d'entre eux échouent en raison de leur complexité . La complexité des logiciels monolithiques croît de façon exponentielle avec la taille. Comme toute croissance exponentielle, la complexité dépasse finalement les ressources disponibles: le fait de consacrer de plus en plus de main-d'œuvre à un projet ne le sauvera pas s'il n'est pas structuré et trop volumineux.

La complexité est réduite grâce à un paradigme d'ingénierie logicielle conçu avant la naissance de la plupart d'entre nous: Divide et impera.

Pour cela, les pièces doivent être faiblement couplées ou ce ne sont pas de vraies pièces indépendantes.

C'est tout ce qu'on peut en dire. Les modificateurs d'accès empêchent un moyen de couplage étroit en ne mettant à la disposition d'un extérieur qu'un sous-ensemble défini de leurs membres. Appliquer cela au niveau du langage plutôt que d'en faire une règle de codage est nécessaire car les programmeurs sont connus pour leur manque d'autodiscipline et les règles de codage ne sont appliquées que par des examens rigoureux.

En plus de tout ce que d'autres ont dit, les modificateurs d'accès sont ou peuvent être une documentation importante. Ils documentent ce qu'est l'API prévue. L'API prévue n'est souvent qu'un petit sous-ensemble de toutes les méthodes définies. Le fait d'avoir des méthodes clairement étiquetées "Non destiné à être utilisé par les clients" permet d'éviter les erreurs et de trouver les bonnes méthodes à utiliser. De plus, ce contrat est vérifié par le compilateur, tant mieux!

3
kutschkem

Les modificateurs d'accès sont-ils importants? Oui, ils le font. Je pense que la réponse avec l'exemple python est assez illustrative. Cependant, il y a quelques pièges à ce sujet, en particulier compte tenu de la façon dont il est souvent utilisé dans le monde Java :

  • Vous devez être honnête avec vos modificateurs d'accès.
  • Ce qui est public sera traité comme public par tout utilisateur de votre classe. Vous ne pourrez peut-être pas modifier ou supprimer des méthodes publiques si les utilisateurs de votre classe ne sont pas sous contrôle
  • Autant que la visibilité, la mutabilité compte.
  • Dans les grands systèmes, le découplage est souvent déjà effectué en utilisant des interfaces qui exposent uniquement les méthodes publiques mais pas les membres ni même les implémentations concrètes

Rien ne complique une classe que d'avoir 10 membres avec un getter et un setter triviaux, souvent générés automatiquement. Il n'est d'aucune façon utile de rendre la variable foo privée et de fournir un setter direct et non contrôlé comme celui-ci.

    class Foobar {
      private Foo bar;

      public void setBar(Foo bar) { this.bar=bar; }
      public Foo getBar() { return bar; }
    }

Dans ce cas, vous n'avez rien gagné sur une variable publique mais avez ajouté deux méthodes à votre classe. Et si tout le monde peut définir directement (presque) tous vos membres, vous ne vous êtes en aucun cas découplé. Dans le même temps, savoir que quelque chose est immuable peut faciliter la vie d'un appelant.

Je recommanderais donc ce qui suit

  • Si vous avez un type primitif et qu'il est immuable, rendez-le public et définitif
  • Pour les méthodes, essayez de vous fier aux interfaces pour masquer les informations, pas aux classes individuelles. Il vous permet d'échanger un tas de classes collaboratrices sans dépendances externes
  • Pour les variables de types complexes, essayez de les passer pendant la construction (si possible). Évitez autant que possible les getters et setters triviaux
2
Manziel