En Java, C # et de nombreux autres langages fortement typés et vérifiés statiquement, nous sommes habitués à écrire du code comme ceci:
public void m1() { ... }
protected void m2() { ... }
private void m2() { ... }
void m2() { ... }
Certaines langues vérifiées dynamiquement ne fournissent pas de mots clés pour exprimer le niveau de "confidentialité" d'un membre de classe donné et s'appuient plutôt sur des conventions de codage. Python, par exemple, préfixe les membres privés avec un trait de soulignement:
_m(self): pass
On peut affirmer que la fourniture de tels mots clés dans des langues vérifiées dynamiquement ajouterait peu d'utilité car elle n'est vérifiée qu'au moment de l'exécution.
Cependant, je ne trouve pas non plus une bonne raison de fournir ces mots clés dans des langues à vérification statique. Je trouve l'exigence de remplir mon code avec des mots clés assez verbeux comme protected
ennuyeux et distrayants. Jusqu'à présent, je n'ai pas été dans une situation où une erreur de compilation générée par ces mots clés m'aurait sauvé d'un bug. Bien au contraire, j'ai été dans des situations où un placement par erreur protected
m'a empêché d'utiliser une bibliothèque.
Dans cet esprit, ma question est:
L'information cache-t-elle plus qu'une convention entre programmeurs utilisée pour définir ce qui fait partie de l'interface officielle d'une classe?
Peut-il être utilisé pour empêcher un état secret d'une classe d'être attaqué? La réflexion peut-elle passer outre ce mécanisme? Qu'est-ce qui en vaut la peine pour le compilateur d' appliquer la dissimulation d'informations?
la source
Réponses:
J'étudie pour la certification Java et tout un tas de problèmes concernent la gestion des modificateurs d'accès. Et ils ont un sens et doivent être utilisés correctement.
J'ai travaillé avec python et, au cours de mon parcours d'apprentissage, j'ai entendu qu'en python, la convention est là parce que les gens qui y travaillent devraient savoir ce que cela signifie et comment l'appliquer. Cela dit,
_m(self): pass
le trait de soulignement m'alerterait de ne pas jouer avec ce domaine. Mais est-ce que tout le monde suivra cette convention? Je travaille avec javascript et je dois dire que non . Parfois, je dois vérifier un problème et la raison en est que la personne faisait quelque chose qu'elle n'était pas censée faire ...lire cette discussion concernant le trait de soulignement python
D'après ce que j'ai dit, oui.
Oui, il peut être utilisé pour sécuriser l'état secret d'une classe et il doit non seulement être utilisé pour cela, mais pour empêcher les utilisateurs de jouer avec l'état des objets afin de changer leurs comportements en quelque chose qu'ils pensent être la manière dont l'objet devrait avoir je travaillais. En tant que développeur, vous devez planifier et réfléchir à cela et concevoir votre classe de manière logique, de manière à ce que son comportement ne soit pas altéré. Et le compilateur, en tant que bon ami, vous aidera à garder le code tel que vous l'avez conçu en appliquant les politiques d'accès.
EDITÉ Oui, la réflexion peut, vérifiez les commentaires
La dernière question est intéressante et je suis prêt à lire les réponses la concernant.
la source
Le spécificateur d'accès "privé" ne concerne pas l'erreur de compilation qu'il génère la première fois que vous le voyez. En réalité, il s'agit de vous empêcher d'accéder à quelque chose qui peut encore changer lorsque l'implémentation de la classe contenant le membre privé change.
En d'autres termes, ne pas vous permettre de l'utiliser quand il fonctionne toujours vous empêche de l'utiliser accidentellement quand il ne fonctionne plus.
Comme Delnan l'a fait remarquer ci-dessous, la convention de préfixe décourage l'utilisation accidentelle de membres susceptibles de changer tant que la convention est respectée et comprise correctement. Pour un utilisateur malveillant (ou ignorant), cela ne l'empêche pas d'accéder à ce membre avec toutes les conséquences possibles. Dans les langues avec prise en charge intégrée des spécificateurs d'accès, cela ne se produit pas dans l'ignorance (erreur du compilateur) et se démarque comme un pouce douloureux lorsqu'il est malveillant (constructions étranges pour arriver au membre privé).
Le spécificateur d'accès "protégé" est une autre histoire - ne pensez pas que ce soit simplement "pas tout à fait public" ou "un peu comme privé". Signifie « protégés » que vous aurez probablement envie d'utiliser cette fonctionnalité lorsque vous dérivez de la classe contenant le membre protégé. Les membres protégés font partie de l '"interface d'extension" que vous utiliserez pour ajouter des fonctionnalités au-dessus des classes existantes sans modifier ces classes existantes elles-mêmes.
Donc, bref récapitulatif:
la source
Si vous écrivez du code qui sera consommé par quelqu'un d'autre, la dissimulation d'informations peut fournir une interface beaucoup plus facile à comprendre. "Quelqu'un d'autre" pourrait être un autre développeur de votre équipe, des développeurs consommant une API que vous avez écrite commercialement, ou même votre futur moi qui "ne se souvient tout simplement pas comment fonctionne le truc". Il est beaucoup plus facile de travailler avec une classe qui n'a que 4 méthodes disponibles que celle qui en a 40.
la source
_member
ouprivate member
dans ma classe est négligeable, tant que l'autre programmeur comprend qu'il ne doit regarder que lespublic
membres ou non préfixés.Je suggère que pour le fond, vous commencez par lire sur les invariants de classe .
Un invariant est, pour faire court, une hypothèse sur l'état d'une classe qui est censée rester vraie pendant toute la durée de vie d'une classe.
Prenons un exemple C # très simple:
Que se passe t-il ici?
addresses
est initialisé lors de la construction de la classe.readonly
, donc rien de l' intérieur ne peut le toucher après la construction (ce n'est pas toujours correct / nécessaire, mais c'est utile ici).Send
méthode peut faire une hypothèse quiaddresses
ne sera jamaisnull
. Il n'a pas à effectuer cette vérification car il est impossible de modifier la valeur.Si d'autres classes étaient autorisées à écrire dans le
addresses
champ (c'est-à-dire si c'était le caspublic
), cette hypothèse ne serait plus valide. Toutes les autres méthodes de la classe qui dépendent de ce champ devraient commencer à effectuer des vérifications null explicites , sinon elles risqueraient de planter le programme.Alors oui, c'est bien plus qu'une "convention"; tous les modificateurs d'accès sur les membres de la classe forment collectivement un ensemble d'hypothèses sur quand et comment cet état peut être changé. Ces hypothèses sont ensuite incorporées dans des membres et des classes dépendants et interdépendants afin que les programmeurs n'aient pas à raisonner sur l'état complet du programme en même temps. La capacité de faire des hypothèses est un élément essentiel de la gestion de la complexité des logiciels.
À ces autres questions:
Oui et non. Le code de sécurité, comme la plupart des codes, va s'appuyer sur certains invariants. Les modificateurs d'accès sont certainement utiles comme panneaux de signalisation pour les appelants de confiance qu'ils ne sont pas censés jouer avec. Le code malveillant ne s'en souciera pas, mais le code malveillant ne doit pas non plus passer par le compilateur.
Bien sûr que oui. Mais la réflexion nécessite que le code appelant ait ce niveau de privilège / confiance. Si vous exécutez du code malveillant avec une confiance totale et / ou des privilèges administratifs, vous avez déjà perdu cette bataille.
Le compilateur déjà fait l' appliquer. Il en va de même pour l'exécution dans .NET, Java et d'autres environnements de ce type - l'opcode utilisé pour appeler une méthode ne réussira pas si la méthode est privée. Les seuls moyens de contourner cette restriction nécessitent un code de confiance / élevé, et le code élevé pourrait toujours simplement écrire directement dans la mémoire du programme. Il est appliqué autant qu'il peut l' être sans nécessiter de système d'exploitation personnalisé.
la source
_
indique le contrat, mais ne l'applique pas. Cela peut rendre difficile l' ignorance du contrat, mais il n'est pas difficile de rompre le contrat, surtout pas par rapport à des méthodes fastidieuses et souvent restrictives comme la réflexion. Une convention dit: "vous ne devriez pas casser cela". Un modificateur d'accès dit "vous ne pouvez pas casser cela". Je n'essaie pas de dire qu'une approche est meilleure que l'autre - elles sont toutes les deux très bien selon votre philosophie, mais ce sont néanmoins des approches très différentes.private
/protected
est comme un préservatif. Vous n'êtes pas obligé d'en utiliser un, mais si vous n'y allez pas, vous feriez mieux de vous assurer de votre timing et de vos partenaires. Cela n'empêchera pas un acte malveillant et pourrait même ne pas fonctionner à chaque fois, mais cela réduira certainement les risques de négligence, et ce, bien plus efficacement que de dire "s'il vous plaît soyez prudent". Oh, et si vous en avez un, mais ne l'utilisez pas, n'attendez pas de sympathie de ma part.La dissimulation d'informations est bien plus qu'une simple convention; essayer de le contourner peut en fait casser la fonctionnalité de la classe dans de nombreux cas. Par exemple, il est assez courant de stocker une valeur dans une
private
variable, de l'exposer à l'aide d'unprotected
oupublic
propriété de la même heure, et dans le getter, de vérifier la valeur null et de faire l'initialisation nécessaire (c'est-à-dire le chargement paresseux). Ou stockez quelque chose dans uneprivate
variable, exposez-la à l'aide d'une propriété, et dans le setter, vérifiez si la valeur a changé et déclenchezPropertyChanging
/PropertyChanged
événements. Mais sans voir l'implémentation interne, vous ne sauriez jamais tout ce qui se passe dans les coulisses.la source
Les informations cachées ont évolué à partir d'une philosophie de conception descendante. Python a été appelé un langage ascendant .
Le masquage des informations est bien appliqué au niveau de la classe en Java, C ++ et C #, donc ce n'est pas vraiment une convention à ce niveau. Il est très facile de faire d'une classe une "boîte noire", avec des interfaces publiques et des détails (privés) cachés.
Comme vous l'avez souligné, en Python, c'est aux programmeurs de suivre la convention de ne pas utiliser ce qui est censé être caché, car tout est visible.
Même avec Java, C ++ ou C #, à un moment donné, la dissimulation d'informations devient une convention. Il n'y a pas de contrôle d'accès aux niveaux d'abstraction les plus élevés impliqués dans les architectures logicielles plus complexes. Par exemple, en Java, vous pouvez trouver l'utilisation de ".internal".noms des packages. Il s'agit purement d'une convention de dénomination, car il n'est pas facile d'appliquer ce type d'informations cachées via l'accessibilité des packages uniquement.
Eiffel est un langage qui s'efforce de définir formellement l'accès. Cet article souligne quelques autres faiblesses masquant les informations de langages tels que Java.
Contexte: La dissimulation d'informations a été proposée en 1971 par David Parnas . Il souligne dans cet article que l'utilisation d'informations sur d'autres modules peut «augmenter de façon désastreuse la connectivité de la structure du système». Selon cette idée, le manque d'informations cachées peut conduire à des systèmes étroitement couplés et difficiles à maintenir. Il poursuit avec:
la source
Ruby et PHP l'ont et l'appliquent au moment de l'exécution.
Le point de dissimulation de l'information est en fait une information qui s'affiche. En «cachant» les détails internes, le but devient apparent d'un point de vue extérieur. Il y a des langues qui embrassent cela. En Java, l'accès par défaut au package interne, dans haXe à protégé. Vous les déclarez explicitement publics pour les exposer.
Le but de tout cela est de rendre vos classes faciles à utiliser en exposant uniquement une interface très cohérente. Vous voulez que le reste soit protégé, afin qu'aucun gars intelligent ne vienne perturber votre état interne pour inciter votre classe à faire ce qu'il veut.
De plus, lorsque les modificateurs d'accès sont appliqués au moment de l'exécution, vous pouvez les utiliser pour appliquer un certain niveau de sécurité, mais je ne pense pas que ce soit une solution particulièrement bonne.
la source
pydoc
supprime_prefixedMembers
de l'interface. Les interfaces encombrées n'ont rien à voir avec le choix d'un mot clé vérifié par le compilateur.Les modificateurs d'accès peuvent certainement faire quelque chose que la convention ne peut pas faire.
Par exemple, en Java, vous ne pouvez accéder à un membre / champ privé que si vous utilisez la réflexion.
Par conséquent, si j'écris l'interface pour un plugin et que je refuse correctement les droits de modifier les champs privés par la réflexion (et de définir le gestionnaire de sécurité :)), je peux envoyer un objet aux fonctions implémentées par n'importe qui et savoir qu'il ne peut pas accéder à ses champs privés.
Bien sûr, il peut y avoir des bogues de sécurité qui lui permettraient de surmonter cela, mais ce n'est pas philosophiquement important (mais en pratique, c'est certainement le cas).
Si l'utilisateur exécute mon interface dans son environnement, il a le contrôle et peut ainsi contourner les modificateurs d'accès.
la source
Il ne s'agit pas tant de sauver les auteurs d'applications des bogues que de laisser les auteurs de bibliothèques décider quelles parties de leur implémentation ils s'engagent à maintenir.
Si j'ai une bibliothèque
Je veux peut-être pouvoir remplacer
foo
la mise en œuvre de et éventuellement changerfooHelper
de façon radicale. Si un tas de gens ont décidé d'utiliserfooHelper
malgré tous les avertissements dans ma documentation, je ne pourrai peut-être pas le faire.private
permet aux auteurs de bibliothèque de diviser les bibliothèques en méthodes de taille gérable (et enprivate
classes d'assistance) sans craindre d'être obligés de conserver ces détails internes pendant des années.Par ailleurs, en Java
private
n'est pas appliqué par le compilateur, mais par le vérificateur de bytecode Java .En Java, non seulement la réflexion peut remplacer ce mécanisme. Il existe deux types de
private
Java. Le type deprivate
cela empêche une classe externe d'accéder aux membres d'une autre classe externe,private
ce qui est vérifié par le vérificateur de bytecode, mais aussi lesprivate
s qui sont utilisés par une classe interne via une méthode d'accesseur synthétique package-private comme dansPuisque la classe
B
(vraiment nomméeC$B
) utilisei
, le compilateur crée une méthode d'accesseur synthétique qui permetB
d'accéderC.i
d'une manière qui dépasse le vérificateur de bytecode. Malheureusement, puisqueClassLoader
vous permet de créer une classe à partir de abyte[]
, il est assez simple de se rendre chez les particuliers quiC
ont exposé aux classes internes en créant une nouvelle classe dansC
le package de 's qui est possible siC
le pot de' s n'a pas été scellé.Une mise en œuvre appropriée
private
nécessite une coordination entre les chargeurs de classe, le vérificateur de bytecode et la politique de sécurité, ce qui peut empêcher l'accès réfléchi aux utilisateurs privés.Oui. La "décomposition sécurisée" est possible lorsque les programmeurs peuvent collaborer tout en préservant chacun les propriétés de sécurité de leurs modules - je n'ai pas à faire confiance à l'auteur d'un autre module de code pour ne pas violer les propriétés de sécurité de mon module.
Les langages de capacités d'objet comme Joe-E utilisent le masquage d'informations et d'autres moyens pour rendre possible une décomposition sécurisée:
Le document lié à partir de cette page donne un exemple de la façon dont
private
la mise en œuvre rend possible une décomposition sécurisée.la source
La dissimulation d'informations est l'une des principales préoccupations d'une bonne conception de logiciels. Découvrez tous les articles de Dave Parnas de la fin des années 70. Fondamentalement, si vous ne pouvez pas garantir la cohérence de l'état interne de votre module, vous ne pouvez rien garantir de son comportement. Et la seule façon de garantir son état interne est de le garder privé et de ne le modifier que par les moyens que vous fournissez.
la source
En intégrant la protection à la langue, vous obtenez quelque chose: une assurance raisonnable.
Si je rend une variable privée, j'ai une assurance raisonnable qu'elle ne sera touchée que par du code au sein de cette classe ou des amis explicitement déclarés de cette classe. La portée du code qui pourrait raisonnablement toucher cette valeur est limitée et explicitement définie.
Existe-t-il maintenant des moyens de contourner la protection syntaxique? Absolument; la plupart des langues en ont. En C ++, vous pouvez toujours convertir la classe en un autre type et pousser ses bits. En Java et C #, vous pouvez vous y refléter. Et ainsi de suite.
Cependant, cela est difficile . Il est évident que vous faites quelque chose que vous ne devriez pas faire. Vous ne pouvez pas le faire par accident (en dehors des écritures sauvages en C ++). Vous devez penser volontiers: "Je vais toucher quelque chose que mon compilateur m'a dit de ne pas faire." Vous devez volontairement faire quelque chose de déraisonnable .
Sans protection syntaxique, un programmeur peut juste accidentellement bousiller les choses. Vous devez enseigner à l'utilisateur une convention, et il doit suivre cette convention à chaque fois . S'ils ne le font pas, le monde devient très dangereux.
Sans protection syntaxique, la responsabilité incombe aux mauvaises personnes: les nombreuses personnes qui utilisent la classe. Ils doivent suivre la convention ou une méchanceté non spécifiée se produira. Grattez cela: une méchanceté non spécifiée peut se produire.
Il n'y a rien de pire qu'une API où, si vous faites la mauvaise chose, tout pourrait fonctionner de toute façon. Cela donne une fausse assurance à l'utilisateur qu'il a fait la bonne chose, que tout va bien, etc.
Et qu'en est-il des nouveaux utilisateurs de cette langue? Non seulement ils doivent apprendre et suivre la syntaxe réelle (appliquée par le compilateur), mais ils doivent maintenant suivre cette convention (appliquée au mieux par leurs pairs). Et s'ils ne le font pas, alors rien de mauvais ne peut se produire. Que se passe-t-il si un programmeur ne comprend pas pourquoi la convention existe? Que se passe-t-il quand il dit «vissez-le» et ne fait que piquer vos soldats? Et que pense-t-il si tout continue à fonctionner?
Il pense que la convention est stupide. Et il ne le suivra plus jamais. Et il dira à tous ses amis de ne pas trop déranger.
la source