web-dev-qa-db-fra.com

Pourquoi avons-nous besoin de variables privées?

Pourquoi avons-nous besoin de variables privées dans les classes?

Chaque livre sur la programmation que j'ai lu dit que c'est une variable privée, c'est ainsi que vous la définissez mais s'arrête là.

Le libellé de ces explications m'a toujours paru comme une véritable crise de confiance dans notre métier. Les explications sonnaient toujours comme si d'autres programmeurs essayaient de gâcher notre code. Pourtant, il existe de nombreux langages de programmation qui n'ont pas de variables privées.

  1. Qu'est-ce que les variables privées aident à empêcher?

  2. Comment décidez-vous si une propriété particulière doit être privée ou non? Si par défaut chaque champ DEVRAIT être privé, alors pourquoi y a-t-il des membres de données publics dans une classe?

  3. Dans quelles circonstances une variable doit-elle être rendue publique?

216
mwallace

Ce n'est pas tant une question de confiance que de gestion de la complexité.

Un membre public est accessible de l'extérieur de la classe, ce qui pour des considérations pratiques signifie "potentiellement n'importe où". Si quelque chose ne va pas avec un champ public, le coupable peut être n'importe où, et donc pour retrouver le bogue, vous devrez peut-être regarder beaucoup de code.

Un membre privé, en revanche, n'est accessible qu'à partir de la même classe, donc si quelque chose ne va pas, il n'y a généralement qu'un seul fichier source à consulter. Si vous avez un million de lignes de code dans votre projet, mais que vos classes sont petites, cela peut réduire votre effort de suivi des bogues d'un facteur 1000.

Un autre avantage est lié au concept de "couplage". Un membre public m d'une classe A utilisée par une autre classe B introduit une dépendance: si vous modifiez m dans A , vous devez également vérifier les utilisations de m dans B. Pire encore, rien dans la classe A ne vous indique où m est utilisé, vous devez donc à nouveau parcourir toute la base de code; si c'est une bibliothèque que vous écrivez, vous devez même vous assurer que le code extérieur votre projet ne se casse pas à cause de votre changement. Dans la pratique, les bibliothèques ont tendance à conserver leurs signatures de méthode d'origine aussi longtemps que possible, quelle que soit la difficulté, puis à introduire un bloc de modifications de rupture avec une mise à jour de version majeure. Avec les membres privés, en revanche, vous pouvez exclure les dépendances immédiatement - elles ne sont pas accessibles de l'extérieur, donc toutes les dépendances sont contenues dans la classe.

Dans ce contexte, les "autres programmeurs" incluent votre moi futur et passé. Il y a de fortes chances que vous sachiez maintenant que vous ne devriez pas faire cette chose X avec votre variable Y, mais vous êtes obligé d'avoir oublié trois mois plus tard lorsqu'un client a besoin de vous de toute urgence pour implémenter une fonctionnalité, et vous vous demandez pourquoi faire X brise Y de manière obscure.

Donc, quand vous devez rendre les choses privées: je dirais que tout est privé par défaut, puis exposer uniquement les parties qui doivent absolument être publiques. Plus vous pouvez faire de privé, mieux c'est.

312
tdammers

Les variables privées aident à empêcher les gens de dépendre de certaines parties de votre code. Par exemple, supposons que vous souhaitiez implémenter une structure de données. Vous voulez que les utilisateurs de votre structure de données ne se soucient pas de la façon dont vous l'avez implémentée, mais utilisent simplement l'implémentation via votre interface bien définie. La raison en est que si personne ne dépend de votre implémentation, vous pouvez la modifier à tout moment. Par exemple, vous pouvez modifier l'implémentation principale pour améliorer les performances. Tous les autres développeurs qui dépendaient de vos implémentations tomberont en panne, tandis que les utilisateurs de l'interface iront bien. Avoir la flexibilité de changer les implémentations sans affecter les utilisateurs de classe est un énorme avantage que l'utilisation de variables privées (et plus largement, encapsulation ) vous donne.

De plus, ce n'est pas vraiment une "crise de confiance". Si vous rendez une donnée publique, vous ne pouvez pas vous assurer que personne n'en dépend. Il est souvent très pratique de dépendre de certaines variables spécifiques à l'implémentation, au lieu de passer par l'interface publique, en particulier dans le feu de l'action. De plus, les développeurs ne se rendront pas toujours compte qu'ils dépendent de quelque chose qui pourrait changer.

Donc, ce genre de réponses à vos autres questions, j'espère. Tous les détails de votre implémentation doivent être privés et la partie publique doit être une petite interface concise et bien définie pour utiliser votre classe.

111
Oleksi

Le mot clé ici est Encapsulation . Dans OOP vous voulez utiliser des variables privées afin d'imposer l'encapsulation appropriée de vos objets/classes.

Bien que d'autres programmeurs ne soient pas là pour vous, ils interagissent avec votre code. Si vous ne rendez pas une variable privée, ils peuvent bien y faire référence dans leur propre code sans vouloir faire de mal du tout. Cependant, si vous devez retourner dans votre classe et changer quelque chose, vous ne savez plus qui utilise quelle variable et où. Le but de l'encapsulation est de rendre l'interface externe de la classe explicite afin que vous sachiez que ces méthodes (généralement) auraient pu être utilisées par d'autres.

  1. Par conséquent, les variables privées garantissent que la variable correspondante reste uniquement dans la classe de définition. Si vous devez le modifier, le changement est local pour la classe elle-même.

  2. Dans les langages traditionnels comme C++ ou Java, vous rendez généralement tout privé et accessible uniquement par les getters et setters correspondants. Pas besoin de vraiment décider.

  3. Parfois, p. Ex. dans une structure C++, vous avez besoin d'une classe uniquement pour regrouper plusieurs éléments. Par exemple, considérons une classe Vector qui a un attribut x et un attribut y uniquement. Dans ces cas, vous pouvez autoriser l'accès direct à ces attributs en les déclarant publics. En particulier, vous ne devez pas vous soucier si du code extérieur modifie un objet de votre classe en écrivant directement de nouvelles valeurs dans x ou y.

Comme point supplémentaire, notez que cette question est considérée comme légèrement différente dans d'autres langues. Par exemple, les langages fortement enracinés dans la programmation fonctionnelle mettent l'accent sur l'immuabilité des données, c'est-à-dire que les valeurs des données ne peuvent pas être modifiées du tout. Dans de tels cas, vous n'avez pas à vous soucier de ce que les autres programmeurs (ou plutôt leur code) font à vos données. Ils ne peuvent tout simplement pas faire quoi que ce soit qui pourrait affecter votre code, car toutes les données sont immuables.

Dans ces langages, vous obtenez donc quelque chose appelé principe d'accès uniforme , où l'on ne distingue pas délibérément des méthodes comme les getters et les setters, mais fournit un accès direct à la variable/fonction. Vous pouvez donc dire que les déclarations privées sont beaucoup plus populaires dans les scénarios orientés objet.

Cela montre également comment l'élargissement de vos connaissances pour inclure d'autres domaines vous fait voir les concepts existants d'une toute nouvelle façon.

26
Frank

Les variables privées garantissent que les références ultérieures en dehors de la portée d'un objet ou d'une fonction n'affecteront pas par inadvertance d'autres variables. Pour les grands projets de programmation, cela peut aider à éviter de nombreux problèmes intéressants (qui ne sont généralement pas détectés par le compilateur).

Par exemple, des langages prototypiques comme Javascript peuvent permettre aux utilisateurs de plâtrer des variables comme bon leur semble:

function SomeObject() {
    this.a = 2; //Public (well, actually protected) variable

    this.showA = function() {
        alert(this.a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 3. Uh oh!

Si cette variable était privée, elle ne pourrait pas être modifiée de l'extérieur:

function SomeObject() {
    var a = 2; //Private variable

    this.showA = function() {
        alert(a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 2

Cela dit, toutes les variables n'ont pas besoin d'être privées et une utilisation excessive de variables privées peut en fait rendre le code plus lourd. Les champs triviaux qui n'ont pas d'impact sérieux sur un objet n'ont pas besoin des méthodes get() et set().

Les variables privées facilitent également l'expansion du code à l'avenir, sans s'appuyer sur une documentation volumineuse sur ce que font les nombreuses variables publiques. Il y a moins de risque de rupture accidentelle de fonctionnalités si moins de variables peuvent être écrasées.

Les variables publiques doivent être utilisées lorsqu'un objet/fonction accède à d'autres objets/fonctions, car en règle générale, les variables privées ne peuvent pas le faire. Vous pouvez avoir de quelques à plusieurs variables publiques, et ce n'est pas une mauvaise chose de les utiliser si elles sont utilisées correctement.

7
Jeffrey Sweeney

Il existe de bonnes réponses citant les avantages pour les futurs responsables et utilisateurs d'une classe. Cependant, la conception originale présente également des avantages. Il fournit un point de démarcation clair entre le moment où votre objectif est de vous faciliter la tâche et celui où votre objectif est de faciliter la tâche de l'utilisateur de la classe. Lorsque vous choisissez entre public et privé, cela signale à votre cerveau de basculer vers un mode ou l'autre, et vous vous retrouvez avec une meilleure API.

Ce n'est pas que les langues sans confidentialité rendent une telle conception impossible, mais moins probable. Au lieu d'être invités à écrire quelque chose comme foo.getBar(), les gens sont enclins à penser qu'un getter n'est pas nécessaire, car il est plus facile d'écrire foo.bar, mais cette décision n'est pas réexaminée lorsque vous vous retrouvez plus tard avec des monstruosités plus longues comme obj.container[baz].foo.bar. Les programmeurs qui n'ont jamais travaillé dans un langage plus strict peuvent même ne pas voir ce qui ne va pas.

C'est pourquoi, dans les langues sans confidentialité, les gens adoptent souvent des normes de dénomination qui le simulent, telles que l'ajout d'un trait de soulignement à tous les membres "privés". C'est un signal utile même lorsque la langue ne l'applique pas.

7
Karl Bielefeldt

Toutes les variables doivent être privées sauf si elles doivent absolument être publiques (ce qui n'est presque jamais le cas, vous devez utiliser les propriétés/getters et setters).

Les variables fournissent en grande partie l'état de l'objet, et les variables privées empêchent les autres d'entrer et de modifier l'état de l'objet.

3
bbb

Voir ce message à une question connexe sur SO .

Le plus court est que la portée variable vous permet de montrer aux consommateurs de votre code ce avec quoi ils devraient et ne devraient pas jouer. Une variable privée peut contenir des données qui ont été "contrôlées" en utilisant un ensemble de propriétés ou une méthode de traitement pour garantir que l'objet entier est dans un état cohérent. La modification directe de la valeur de la variable privée peut rendre l'objet incohérent. En le rendant privé, quelqu'un doit travailler très dur et disposer d'autorisations d'exécution très élevées pour le changer.

La portée appropriée des membres est donc essentielle dans le code auto-documenté.

2
KeithS

Cela n'a rien à voir avec la confiance ou la peur des attaques, il s'agit uniquement d'encapsulation - ne forçant pas des informations inutiles sur l'utilisateur de la classe.

Considérez les constantes privées - elles ne doivent pas contenir de valeurs secrètes (celles-ci doivent être stockées ailleurs), elles ne peuvent pas être modifiées, elles n'ont pas besoin d'être passées dans votre classe (sinon elles devraient être publiques). La seule utilisation possible pour eux est comme constantes dans AUTRES classes. Mais si vous faites cela, ces classes dépendent maintenant de votre classe, pour effectuer un travail sans rapport avec votre classe. Si vous modifiez la constante, d'autres classes peuvent se casser. C'est mauvais des deux côtés - en tant qu'écrivain de votre classe, vous voulez que la liberté change autant que possible, et ne voulez pas vous soucier de choses hors de votre contrôle. Un consommateur de votre classe veut pouvoir compter sur les détails exposés de votre classe, sans se soucier de le changer et de casser son code.

Un consommateur de votre classe veut savoir tout le nécessaire pour INTERAGIR avec votre classe, et ne veut rien savoir de votre classe qui ne change pas la façon dont il le fait: c'est une futilité inutile. Si vous avez utilisé un langage avec réflexion, combien de fois l'avez-vous utilisé pour apprendre non pas comment une classe fait quelque chose ou où elle lève une exception de manière inattendue, mais simplement le nom des champs et méthodes privés? Je parie jamais. Parce que vous n'avez aucune utilité pour cette connaissance.

2
jmoreno
  • Qu'est-ce que les variables privées aident à empêcher?

Il s'agit d'indiquer clairement quelles propriétés et méthodes sont une interface et lesquelles sont en fait des fonctionnalités de base. Les méthodes/propriétés publiques concernent un autre code, souvent le code d'un autre développeur utilisant vos objets. Je n'ai jamais travaillé sur des équipes de plus de 100 sur le même projet, mais d'après mon expérience de peut-être des équipes de 3 à 5 avec jusqu'à 20 autres développeurs utilisant des choses que j'ai écrites, les autres préoccupations semblent stupides.

Remarque: je suis principalement un développeur JavaScript. Je ne m'inquiète généralement pas des autres développeurs qui consultent mon code, et je suis pleinement conscient qu'ils pourraient simplement redéfinir mes données à tout moment. Je suppose qu'ils sont suffisamment compétents pour savoir ce qu'ils font en premier. Sinon, il est peu probable que je travaille dans une équipe qui n'utilise pas le contrôle de code source.

  • Comment décidez-vous si un ensemble particulier de propriétés doit être privé ou non? Si par défaut chaque champ DEVRAIT être privé, alors pourquoi y a-t-il des membres de données publics dans une classe?

J'avais l'habitude de penser qu'il était stupide de mettre des getters et des setters sur des propriétés privées alors que vous n'effectuiez pas vraiment de validation ou autre traitement spécial de la propriété à définir. Je ne pense toujours pas que ce soit toujours nécessaire, mais lorsque vous traitez avec une grande échelle, une grande complexité, mais surtout de nombreux autres développeurs s'appuyant sur le comportement cohérent de vos objets, il peut être utile de faire les choses dans un manière cohérente et uniforme. Lorsque les gens voient des méthodes appelées getThat et setThat, ils savent exactement quelles sont les intentions et ils peuvent écrire leurs objets pour interagir avec les vôtres en espérant qu'ils obtiendront toujours des tomates plutôt que des tomahtos. . S'ils ne le font pas, ils savent que votre objet leur donne quelque chose qu'il ne devrait probablement pas faire, ce qui signifie qu'il permet à un autre objet de faire quelque chose avec ces données qu'il ne devrait probablement pas. Vous pouvez contrôler cela au besoin.

L'autre grand avantage est qu'il est plus facile de faire en sorte que vos objets transmettent ou interprètent des valeurs différemment d'un getter ou d'un setter en fonction d'une sorte de contexte d'état variable. Parce qu'ils accèdent à vos trucs avec une méthode, il est beaucoup plus facile de changer la façon dont votre objet fonctionne plus tard sans casser tout autre code qui en dépend. Le principe important étant que seul votre objet modifie réellement les données dont vous lui avez confié la responsabilité. Cela est particulièrement important pour garder votre code faiblement couplé, ce qui est une énorme victoire en termes de portabilité et de facilité de modification.

  • Dans quelles circonstances une variable doit-elle être rendue publique?

Avec des fonctions de première classe (vous pouvez passer des fonctions comme arguments), je ne peux pas penser à beaucoup de bonnes raisons autres que ce n'est pas vraiment nécessaire de le faire sur des projets à plus petite échelle. Sans cela, je suppose que vous pourriez avoir des membres qui sont traités de manière intensive et régulière par d'autres objets au point qu'il semble un peu cruel d'appeler constamment get et définit un objet qui ne touche pas vraiment ces données lui-même, mais cela dans lui-même me ferait me demander pourquoi l'objet était responsable de ces données. De manière générale, j'aurais tendance à vouloir réexaminer mon architecture pour détecter les failles avant de décider qu'une propriété de données publiques est nécessaire. OMI, il n'y a rien de mal à une langue vous permettant de faire quelque chose qui est généralement une mauvaise idée. Certaines langues ne sont pas d'accord.

2
Erik Reppen

Je vais parler de la valeur de l'encapsulation sous un angle différent en utilisant un exemple réel. Il y a un certain temps (début des années 80), un jeu a été écrit pour le Radio Shack Color Computer appelé Dungeons of Daggorath. Il y a quelques années, Richard Hunerlach l'a porté d'une liste d'assembleurs papier vers C/C++ ( http://mspencer.net/daggorath/dodpcp.html ). Quelque temps plus tard, j'ai obtenu le code et j'ai commencé à le re-factoriser pour donner une meilleure encapsulation, ( http://dbp-consulting.com/stuff/ ).

Le code était déjà pris en compte dans différents objets pour gérer la planification, la vidéo, les entrées utilisateur, la création de donjons, les monstres, etc. mais on pouvait certainement dire qu'il était porté depuis l'assembleur. Ma plus grande tâche était d'augmenter l'encapsulation. J'entends par là obtenir différentes parties du code pour se mettre le nez sur les affaires des autres. Il y avait de nombreux endroits par exemple, qui changeraient directement les variables vidéo pour avoir un effet. Certains l'ont fait avec vérification des erreurs, d'autres non, certains avaient des idées subtilement différentes de ce que signifiait changer cette chose. Il y avait beaucoup de duplication de code. Au lieu de cela, la section vidéo devait avoir une interface pour accomplir les tâches souhaitées, et le code pour le faire pourrait être tout en un seul endroit, une seule idée, un seul endroit pour déboguer. Ce genre de chose était endémique. Chaque objet accédait directement à tous les autres objets et le code était dupliqué partout.

Alors que le code commençait à être démêlé, des bogues qui n'avaient même pas été diagnostiqués ont disparu. Le code est devenu de meilleure qualité. Chaque tâche est devenue la responsabilité d'un seul endroit dans le code et il n'y avait qu'un seul endroit pour bien faire les choses. (J'en trouve encore plus à chaque fois que je refasse le code.)

Tout ce qui est visible sur votre code est votre interface. Que vous le vouliez ou non. Si vous limitez autant que possible la visibilité, vous ne serez pas tenté de mettre le code au mauvais endroit plus tard. Il indique clairement quelle partie du code est responsable de quelles données. Il permet un design meilleur, plus propre, plus simple et plus élégant.

Si vous rendez un objet responsable de ses propres données et fournissez des interfaces à tous ceux qui ont besoin de quelque chose, votre code devient beaucoup plus simple. Ça tombe juste. Vous avez de petites routines simples qui ne font qu'une chose. Moins de complexité == moins de bugs.

2
phorgan1

Le OOP a l'héritage a une de ses fonctionnalités (Java ou C++). Donc si nous allons hériter (ce qui signifie que nous allons accéder aux variables de la classe héritée), il y a la possibilité de affectant ces variables. Nous devons donc décider si les variables peuvent être modifiées ou non.

Dans ce but uniquement, nous utilisons des modificateurs d'accès dans la POO. L'un des modificateurs est privé, ce qui signifie qu'il n'est accessible que par cette classe. Aucune autre classe ne peut affecter ces variables.

Comme nous le savons, protégé signifie qu'il est accessible à la classe qui va hériter.

La raison pour laquelle il existe un concept de modificateur dans la POO est due à ces raisons (n'importe quelle classe peut accéder à d'autres variables de classe à l'aide du concept OOP). S'il n'y a pas de concept de modificateur, cela signifie qu'il aurait été difficile de héritant de classes ou utilisant d'autres concepts OOP.

1
rajNaveen

Comme indiqué par d'autres, les variables privées sont bonnes pour éviter les mauvaises utilisations conduisant l'objet dans un état incohérent et les bogues difficiles à suivre et les exceptions imprévues.

Mais d'autre part, ce qui a été le plus ignoré par les autres concerne les champs protégés.

Une sous-classe étendue aura un accès complet aux champs protégés, ce qui rend l'objet aussi fragile que si ces champs étaient publics, mais cette fragilité est limitée à la classe d'extension elle-même (à moins qu'elle ne les expose encore plus).

Ainsi, les champs publics sont difficiles à considérer comme bons, et à ce jour, la seule raison de les utiliser est pour les classes utilisées comme paramètre de configuration (une classe très simple avec de nombreux champs et sans logique, de sorte que la classe est passée en tant que paramètre seul à une méthode).

Mais d'un autre côté, les champs privés diminuent la flexibilité de votre code pour les autres utilisateurs.

Flexibilité vs problèmes, avantages et inconvénients:

Les objets instanciés par votre code dans la classe Vanilla avec des champs protégés sont sûrs et relèvent de votre seule responsabilité.

En revanche, les objets étendant votre classe avec des champs protégés, instanciés par les utilisateurs de votre code, sont de leur responsabilité, pas de la vôtre.

Ainsi, les champs/méthodes protégés mal documentés, ou si les utilisateurs ne comprennent pas vraiment comment ces champs et méthodes doivent être utilisés, ont de bonnes chances de causer des problèmes inutiles à eux-mêmes et à vous-même.

En revanche, rendre la plupart des choses privées réduira la flexibilité des utilisateurs, et peut même les éloigner à la recherche d'alternatives maintenues, car ils peuvent ne pas vouloir créer et maintenir une fourchette juste pour que les choses se passent comme prévu.

Donc, un bon équilibre entre privé, protégé et public est ce qui compte vraiment.

Maintenant, décider entre privé et protégé est le vrai problème.

Quand utiliser protégé?

Chaque fois que vous comprenez qu'un champ peut être très flexible, il doit être codé comme protégé. Cette flexibilité est: de devenir null (où null est toujours vérifié et reconnu comme un état valide ne lançant pas d'exceptions), d'avoir des contraintes avant d'être utilisé par votre classe ex. > = 0, <100, etc., et automatiquement corrigé pour les valeurs de dépassement/dépassement, lançant tout au plus un message d'avertissement.

Donc, pour un tel champ protégé, vous pouvez créer un getter et l'utiliser uniquement (au lieu d'utiliser directement la variable de champ), tandis que d'autres utilisateurs peuvent ne pas l'utiliser, au cas où ils voudraient plus de flexibilité pour leur code spécifique, dans mon exemple, cela pourrait être comme : s'ils veulent que les valeurs négatives fonctionnent correctement dans leur classe étendue.

1
Aquarius Power

imo @tdammers n'a pas raison et est en fait trompeur.

Des variables privées existent pour masquer les détails de l'implémentation. Votre classe A utilise peut-être un array pour stocker les scores. Demain, vous voudrez peut-être utiliser un tree ou priority queue À la place. Tous les utilisateurs de votre classe ont besoin d'un moyen de saisir les scores et les noms addScore() et un moyen de déterminer qui sont les 10 premiers getTop(n).

Ils n'ont pas besoin d'accéder à la structure de données sous-jacente. Super? Eh bien, il y a quelques mises en garde.

De toute façon, vous ne devriez pas stocker beaucoup d'état, la plupart d'entre eux ne sont pas nécessaires. Le stockage de l'état compliquera votre code même lorsqu'il est "séparé" d'une classe spécifique. Pensez-y, cette variable privée a probablement changé parce qu'un autre objet a appelé une méthode de cette classe. Vous devez toujours déterminer quand et où cette méthode a été appelée et cette méthode publique peut être appelée n'importe où théoriquement.

La meilleure chose que vous puissiez faire est de limiter la quantité d'état que contient votre programme et d'utiliser des fonctions pures lorsque cela est possible.

0
AturSams