Pourquoi avoir des champs privés, n'est pas assez protégé?

265

La visibilité privatedes champs, propriétés et attributs de classe est-elle utile? En POO, tôt ou tard, vous allez créer une sous-classe d'une classe et dans ce cas, il est bon de comprendre et de pouvoir complètement modifier la mise en œuvre.

L'une des premières choses que je fais lorsque je sous-classe une classe consiste à changer un tas de privateméthodes en protected. Cependant, il est important de cacher les détails du monde extérieur . Nous avons donc besoin de cela, protectedet pas seulement public.

Ma question est la suivante: connaissez-vous un cas d’utilisation important où privateau lieu d’ protectedêtre un bon outil, ou deux options " protected& public" seraient-elles suffisantes pour les langues POO?

Adam Libuša
la source
236
Pour les votants: Bien que je sois aussi fortement en désaccord avec les prémisses du PO, je vote en faveur de cette question, car elle est parfaitement cohérente et mérite une réponse. Oui, il faut expliquer au PO pourquoi cela est faux, mais la façon de le faire est d’ écrire une réponse (ou de suggérer des modifications aux réponses existantes), et non de réduire le vote juste parce qu’il ne l’a pas encore compris.
Ixrec
18
Les classes dérivées font partie du monde extérieur.
CodesInChaos
20
N'oubliez pas que protégé ne signifie pas toujours que l'accès est verrouillé sur la hiérarchie des héritages. En Java, il accorde également un accès au niveau du paquet.
Berry120
8
Mon professeur disait: "Il y a des choses que je ne dirais pas à mes enfants. Celles-ci sont mes champs privés."
Samedi

Réponses:

225

Comme vous le dites, vous avez protectedtoujours la possibilité de "modifier complètement l'implémentation". Cela ne protège réellement rien dans la classe.

Pourquoi nous soucions-nous de "véritablement protéger" le contenu de la classe? Sinon, il serait impossible de modifier les détails de l'implémentation sans casser le code client . En d'autres termes, les personnes qui écrivent des sous-classes sont également "le monde extérieur" pour celui qui a écrit la classe de base d'origine.

En pratique, les protectedmembres sont essentiellement une "API publique pour sous-classes" d'une classe et doivent rester stables et rétrocompatibles tout autant que les publicmembres. Si nous n'avions pas la capacité de créer de vrais privatemembres, rien dans une implémentation ne pourrait jamais être modifié en toute sécurité, car vous ne pourriez pas exclure la possibilité que le code client (non malveillant) ait réussi à dépendre d'une manière ou d'une autre. il.

Incidemment, alors que "En OOP, tôt ou tard, vous allez créer une sous-classe d'une classe" est techniquement vrai, votre argument semble faire l'hypothèse bien plus forte que "tôt ou tard, vous allez créer une sous-classe de chaque classe "ce qui n’est presque certainement pas le cas.

Ixrec
la source
11
Je vous jetterais plus de +1 si je pouvais, car c'est la première fois que cela a du privatesens pour moi. La perspective lib doit être utilisée plus souvent, sinon toute mentalité de codage «contrôle absolu, responsabilité absolue» (semblable à celle du C) pourrait le signaler comme «me protège de moi-même». Notez que j’avais toujours utilisé privateprécédemment, j’ai toujours senti que la bonne documentation et une convention de nommage, comme _foopour indiquer que vous ne devriez probablement pas jouer, étaient équivalentes, sinon meilleures. Pouvoir dire de façon déterministe que «rien ne va casser» est une privatecaractéristique légitime, seulement.
abluejelly
A l'origine, j'avais ignoré le cas du code des bibliothèques publiques et des frameworks et ne pensais plus ou moins qu'en termes de "code client". L’optimisation de la mise en œuvre interne est un bon exemple de ma question, bien que je me demande si cela se produit réellement (en particulier lorsque de nombreuses personnes recommandent qu’une classe ne dépasse pas 1000 lignes de code). En général, j'aime bien l'approche de Ruby, où privé est en quelque sorte une recommandation: "Sois ici des dragons, agis avec prudence".
Adam Libuša
9
@ AdamLibuša Même s'il s'agit d'un contrat beaucoup plus important si votre code est public, il s'applique même si vous êtes l'auteur de tous les clients de la classe. Le problème passe simplement du fait que certains refactors sont impossibles à certains refacteurs fastidieux et sujets aux erreurs. L’optimisation est en fait la raison la moins fréquente de ces refactors dans mon expérience (bien que je fasse principalement du Javascript), c’est plutôt un bogue qui expose une faille fondamentale d’implémentation qui nécessite de restructurer le graphe de dépendance / d’appel des différents bits internes afin d’atteindre une solution vraiment robuste.
Ixrec
5
"" tôt ou tard, vous allez faire une sous-classe de chaque classe "n'est presque certainement pas le cas." Et plus précisément, vous n'allez certainement pas, et vous ne DEVRIEZ PAS, annuler toutes les fonctions d'une classe et modifier l'utilisation de chaque élément de données d'une classe. Certaines choses devraient logiquement être gravées dans le marbre pour que la classe ait un sens.
Jay
1
Je vous jetterais plus de +1 si je pouvais => C'est ce que nous appelons une prime @abluejelly
Thomas Ayoub
256

En POO, tôt ou tard, vous allez créer une sous-classe d'une classe

C'est faux. Toutes les classes ne sont pas censées être sous-classées et certains langages POO à typage statique possèdent même des fonctionnalités pour le prévenir, par exemple final(Java et C ++) ou sealed(C #).

il est bon de comprendre et de pouvoir modifier complètement la mise en œuvre.

Non ce n'est pas. Il est bon qu'une classe puisse définir clairement son interface publique et préserver ses invariants, même si elle en est héritée.

En général, le contrôle d'accès concerne la compartimentation. Vous souhaitez comprendre une partie individuelle du code sans avoir à comprendre en détail comment elle interagit avec le reste du code. L'accès privé le permet. Si tout est au moins protégé, vous devez comprendre ce que fait chaque sous-classe afin de comprendre le fonctionnement de la classe de base.

Ou pour le dire dans les termes de Scott Meyers: les parties privées d’une classe sont affectées par une quantité finie de code: le code de la classe elle-même.

Les parties publiques sont potentiellement affectées par chaque bit de code existant et par tout bit de code restant à écrire, ce qui représente une quantité infinie de code.

Les parties protégées sont potentiellement affectées par chaque sous-classe existante et chaque sous-classe non encore écrite, ce qui représente également une quantité infinie de code.

La conclusion est que protégé ne vous apporte guère plus que le public, alors que privé vous apporte une réelle amélioration. C'est l'existence du spécificateur d'accès protégé qui est discutable et non privée.

Sebastian Redl
la source
34
+1 pour le scénario théorique de "affectés par une quantité infinie de code"
Broken_Window
13
Il convient peut-être également de noter que les concepteurs C # ont en fait décidé d'interdire de remplacer une méthode héritée, à moins que celle-ci ne soit explicitement marquée comme virtual, pour les raisons exposées dans cette réponse.
Will Vousden
11
Ou simplement: protectedest pour les méthodes, pas pour les membres de données.
Matthieu M.
7
Je ne peux pas le trouver maintenant, mais je me souviens d'avoir lu une réponse bien écrite de @EricLippert sur les raisons pour lesquelles MS était une sealedpartie importante de la bibliothèque de .net et par l'IIRC pourquoi il aurait aimé enfermer le reste. Dès que vous autorisez des héritiers tiers à toucher des valeurs internes, vous devez ajouter un nombre considérable de contrôles de validation / validité à chaque méthode, car vous ne pouvez plus faire confiance aux invariants de conception concernant l'état interne des objets.
Dan Neely
4
@ ThorbjørnRavnAndersen "en développement, mais pas après sa publication" - comment une classe est-elle supposée le savoir? Voulez-vous vraiment compiler une version test différente de la version publiée alors que vous ne pouvez même pas tester la version? Les tests ne devraient de toute façon pas avoir besoin d'accéder à des données privées.
Sebastian Redl
33

Oui, les champs privés sont absolument nécessaires. Cette semaine, j’avais besoin d’écrire une implémentation de dictionnaire personnalisée dans laquelle je contrôlais ce qui était mis dans le dictionnaire. Si le champ du dictionnaire devait être protégé ou rendu public, les contrôles que j'avais si soigneusement écrits auraient pu être facilement contournés.

Les champs privés servent généralement à fournir des garanties que les données sont celles attendues par le codeur d'origine. Rendez tout protégé / public et vous montez un entraîneur et des chevaux à travers ces procédures et validation.

Robbie Dee
la source
2
+1 pour "chevaucher un entraîneur et ses chevaux". C'est une excellente phrase que j'aimerais avoir plus.
Nic Hartley
2
Si quelqu'un a besoin de classer votre classe, peut-être devrait-il avoir accès à vos sauvegardes? Et si vous ne voulez pas que quelqu'un modifie vos sauvegardes pour atteindre un objectif, peut-être ne partagez-vous pas le code? Cela fonctionne comme ça en Ruby - private est plus ou moins une recommandation.
Adam Libuša
3
@ AdamLibuša "Ne partagez pas le code"? Dès que vous publiez une DLL, vous partagez le code - toutes les structures et méthodes et tout ce que le monde entier peut voir, en particulier avec les langages qui prennent en charge la réflexion par défaut. Tout le monde peut faire ce qu’il veut avec «votre code» - privatec’est simplement une façon de dire «ne les touchez pas» et qu’il soit appliqué dans le contrat du compilateur. Dans des systèmes tels que .NET, cela a également des implications importantes en matière de sécurité - vous ne pouvez toucher les parties privées des autres que lorsque vous avez une confiance totale (l'équivalent d'un accès administrateur / racine).
Luaan
2
@ AdamLibuša Je pense que votre confusion provient principalement des différentes approches POO adoptées par les différentes langues. La racine de la programmation orientée objet (telle que définie à l'origine) est la messagerie - cela signifie que tout est privé, à l'exception des messages auxquels vous répondez. Dans la plupart des langues OOPish, cela est présenté comme "conservez vos données confidentielles" - un moyen de rendre l'interface publique aussi petite que possible. La seule façon pour l'utilisateur (que ce soit une sous-classe ou une autre classe) de manipuler votre classe consiste à utiliser l'interface publique que vous avez définie - de la même manière que vous utilisez habituellement le volant et les pédales pour conduire votre voiture :)
Luaan
3
@Luaan +1 pour quand il est permis de toucher les parties privées des autres!
Nigel Touch
12

Lorsqu’on tente de raisonner formellement sur l’exactitude d’un programme orienté objet, il est typique d’utiliser une approche modulaire impliquant des invariants d’objet . Dans cette approche

  1. Les méthodes leur ont associé des conditions préalables et postérieures (contrats).
  2. Les objets leur sont associés des invariants.

Le raisonnement modulaire sur un objet se déroule comme suit (à une première approximation au moins)

  1. Prouver que le constructeur de l'objet établit l'invariant
  2. Pour chaque méthode non privée, supposons que l'invariant d'objet et la précondition de méthode conservent l'entrée, puis prouvent que le corps du code implique que la postcondition et l'invariant conservent la sortie de la méthode

Imaginez que nous vérifions l’ object Autilisation de l’approche ci-dessus. Et souhaitent maintenant vérifier method gde object Bqui appelle method fde object A. Le raisonnement modulaire nous permet de raisonner method gsans avoir à reconsidérer la mise en œuvre de method f. Si nous pouvons établir l'invariant de object Aet la condition préalable de method fsur le site de l'appel, method g,nous pouvons prendre la condition de post de method fcomme récapitulatif du comportement de l'appel de méthode. De plus, nous saurons également qu’après le retour de l’appel, l’invariant de Astill est maintenu.

Cette modularité du raisonnement est ce qui nous permet de penser formellement aux grands programmes. Nous pouvons raisonner individuellement sur chacune des méthodes, puis composer les résultats de ce raisonnement pour raisonner à son tour sur de plus grandes parties du programme.

Les champs privés sont très utiles dans ce processus. Pour savoir que l'invariant d'un objet continue de tenir entre deux appels de méthode sur cet objet, nous nous appuyons généralement sur le fait que l'objet n'est pas modifié dans l'intervalle.

Pour que le raisonnement modulaire fonctionne dans un contexte où les objets n'ont pas de champs privés, nous devons pouvoir nous assurer que, quel que soit le champ paramétré par un autre objet, l'invariant est toujours rétabli (après le champ ensemble). Il est difficile d'imaginer un invariant d'objet valable pour les deux, quelle que soit la valeur des champs de l'objet, et qui soit également utile pour raisonner sur l'exactitude du programme. Nous devrions probablement inventer une convention compliquée concernant l'accès aux champs. Et probablement aussi perdre une partie (au pire, même la totalité) de notre capacité à raisonner de manière modulaire.

Champs protégés

Les champs protégés restaurent une partie de notre capacité à raisonner de manière modulaire. En fonction de la langue, protectedla capacité de définir un champ sur toutes les sous-classes ou toutes les sous-classes et les mêmes classes peut être restreinte. Il arrive souvent que nous n’ayons pas accès à toutes les sous-classes lorsque nous raisonnons sur l’exactitude d’un objet que nous écrivons. Par exemple, vous écrivez peut-être un composant ou une bibliothèque qui sera utilisé ultérieurement dans un programme plus important (ou dans plusieurs programmes plus importants), dont certains n'ont peut-être même pas encore été écrits. En règle générale, vous ne saurez pas si et de quelle manière il peut être sous-classé.

Cependant, il incombe généralement à une sous-classe de maintenir l’objet invariant de la classe qu’elle étend. Ainsi, dans un langage où protéger signifie uniquement "sous-classe" et où nous sommes disciplinés pour veiller à ce que les sous-classes conservent toujours les invariants de leur super-classe, vous pouvez affirmer que le choix d'utiliser protégé au lieu de privé ne perd que peu de modularité. .

Bien que je parle de raisonnement formel, on pense souvent que lorsque les programmeurs raisonnent de manière informelle sur l'exactitude de leur code, ils s'appuient parfois sur des types d'arguments similaires.

flamingpenguin
la source
8

privateles variables dans une classe sont meilleures que protectedpour la même raison qu'une breakdéclaration à l'intérieur d'un switchbloc est meilleure qu'une goto labeldéclaration; ce qui est que les programmeurs humains sont sujettes aux erreurs.

protectedles variables se prêtent à des abus non intentionnels (erreurs de programmation), tout comme l' gotoinstruction se prête à la création de code spaghetti.

Est-il possible d'écrire du code de travail sans bug en utilisant protecteddes variables de classe? Oui bien sûr! Tout comme il est possible d'écrire du code de travail sans bug en utilisant goto; mais comme le dit le cliché, "Ce n’est pas parce que vous le pouvez que vous devriez!"

Les classes, et en fait le paradigme OO, existent pour se protéger contre les programmeurs humains malchanceux et enclins à l'erreur qui font des erreurs. La défense contre les erreurs humaines est aussi efficace que les mesures de défense intégrées dans la classe. Réaliser la mise en œuvre de votre classe protectedéquivaut à faire un énorme trou dans les murs d'une forteresse.

Les classes de base n'ont absolument aucune connaissance des classes dérivées. En ce qui concerne une classe de base, protectedcela ne vous donne pas plus de protection que public, car rien n'empêche une classe dérivée de créer un publicgetter / setter qui se comporte comme une porte dérobée.

Si une classe de base autorise un accès sans entrave à ses détails d'implémentation internes, il devient alors impossible pour la classe elle-même de se défendre contre les erreurs. Les classes de base n'ont absolument aucune connaissance de leurs classes dérivées et n'ont donc aucun moyen de se prémunir contre les erreurs commises dans ces classes dérivées.

La meilleure chose à faire pour une classe de base est de masquer le plus possible son implémentation privateet de mettre en place suffisamment de restrictions pour ne pas interrompre les modifications des classes dérivées ou de tout autre élément en dehors de la classe.

En fin de compte, les langages de haut niveau existent pour minimiser les erreurs humaines. De bonnes pratiques de programmation (telles que les principes SOLID ) existent également pour minimiser les erreurs humaines.

Les développeurs de logiciels qui ignorent les bonnes pratiques de programmation ont un risque d'échec beaucoup plus élevé et sont plus susceptibles de produire des solutions non maintenues cassées. Ceux qui suivent les bonnes pratiques ont beaucoup moins de chances d’échouer et sont plus susceptibles de proposer des solutions fonctionnelles et maintenables.

Ben Cottrell
la source
Pour réduire les électeurs - qu'est-ce qui pourrait être changé / amélioré dans cette réponse?
Ben Cottrell
Je n'ai pas voté bas, mais en comparant le niveau d'accès avec break / goto?
imel96
@ imel96 Non, en comparant la raison pour laquelle ils sont évités et découragés par les "meilleures pratiques" (en particulier pour l'écriture de nouveau code). C'est-à-dire qu'un programmeur compétent éviterait les publicdétails d'implémentation car il se prête au code non maintenable. Un programmeur compétent devrait éviter gotocar il se prête à un code non maintenable. Cependant, le monde réel est tel que, parfois, vous êtes perdu dans un fouillis de code hérité et que vous n'avez d'autre choix que d'utiliser goto. Pour cette même raison, vous n'avez parfois d'autre choix que d'utiliser des détails d'implémentation publics / protégés.
Ben Cottrell
La gravité est de magnitude différente, c'est incomparable. Je pourrais vivre avec du code qui n'a que des publicpropriétés / interface, mais pas avec du code qui utilise seulement goto.
imel96
2
@ imel96 Avez-vous déjà travaillé avec du code qui utilise gotoou est-ce simplement parce que vous avez lu de tels articles? Je comprends pourquoi goto c'est mal parce que j'ai passé 2 ans à travailler dans un code ancien qui l'utilise; et j'ai passé encore plus de temps à travailler dans le code où les détails de l'implémentation des "classes" étaient divulgués partout publicet les friendspécificateurs utilisés comme des hacks rapides / faciles / sales. Je n'accepte pas l'argument selon lequel "exposer publiquement les détails de la mise en oeuvre" ne peut pas causer un gâchis spaghetti entremêlé du même niveau de gravité que gotoparce qu'il peut absolument.
Ben Cottrell
4

Les classes héritables ont deux contrats - un avec les détenteurs de références d’objet et avec les classes dérivées. Les membres du public sont liés par le contrat avec les titulaires de référence et les membres protégés sont liés par le contrat avec les classes dérivées.

La création de membres en protectedfait une classe de base plus polyvalente, mais limite souvent les modifications possibles des versions futures de la classe. La création de membres privatedonne à l'auteur de la classe plus de souplesse pour modifier le fonctionnement interne de la classe, mais limite le type de classes pouvant en être utilement dérivées.

Par exemple, List<T>dans .NET, le magasin de sauvegarde est privé; s'il était protégé, les types dérivés pourraient faire des choses utiles qui seraient autrement impossibles, mais les futures versions de List<T>devraient toujours utiliser son magasin de sauvegarde monolithique maladroit même pour les listes contenant des millions d'éléments. Rendre le magasin de stockage privé permettrait aux futures versions de de List<T>faire appel à un magasin de stockage plus efficace sans casser les classes dérivées.

supercat
la source
4

Je pense que votre argument repose sur une hypothèse clé selon laquelle, lorsque quelqu'un écrit un cours, il ne sait pas qui pourrait l'étendre ultérieurement et pour quelle raison . Compte tenu de cette hypothèse, votre argument aurait tout son sens, car chaque variable que vous définissez comme privée pourrait alors potentiellement couper toute voie de développement à l'avenir. Cependant, je rejetterais cette hypothèse.

Si cette hypothèse est rejetée, il n'y a que deux cas à considérer.

  1. L’auteur de la classe d’origine avait des idées très claires sur les raisons de son extension (par exemple, c’est un BaseFoo et il y aura plusieurs implémentations concrètes de Foo plus tard).

Dans ce cas, l'auteur sait que quelqu'un étendra la classe et pourquoi et saura donc exactement ce qu'il faut protéger et ce qu'il faut rendre privé. Ils utilisent la distinction private / protected pour communiquer une interface de type à l'utilisateur créant la sous-classe.

  1. L'auteur de la classe enfant tente de pirater un comportement dans une classe parent.

Ce cas devrait être rare (vous pourriez soutenir qu'il n'est pas légitime) et n'est pas préférable à la simple modification de la classe d'origine dans la base de code d'origine. Cela pourrait aussi être un symptôme d'une mauvaise conception. Dans ces cas, je préférerais que la personne piratant le comportement utilise simplement d’autres piratages tels que des amis (C / C ++) et setAccessible(true)(Java).

Je pense qu'il est prudent de rejeter cette hypothèse.

Cela revient généralement à l'idée de la composition par rapport à l'héritage . L'héritage est souvent considéré comme un moyen idéal de réduire la réutilisation du code, mais il devrait rarement être le premier choix en matière de réutilisation du code. Je n'ai pas un simple argument renversant et il peut être assez difficile et compliqué à comprendre. Cependant, dans mon expérience de la modélisation de domaine, j'ai constaté que j'utilise rarement l'héritage sans bien comprendre qui va hériter de ma classe et pourquoi.

Rythme
la source
2

Les trois niveaux d'accès ont leur cas d'utilisation, la POO serait incomplète s'il n'en manquait aucun. D'habitude tu fais

  • rendre toutes les variables / données membres privées . Vous ne voulez pas que quelqu'un de l'extérieur gâche vos données internes. Les méthodes qui fournissent une fonctionnalité auxiliaire (pensez à des calculs basés sur plusieurs variables de membre) à votre interface publique ou protégée - ceci est uniquement destiné à un usage interne, et vous voudrez peut-être la modifier / l’améliorer ultérieurement.
  • Rendez publique l' interface générale de votre classe . C’est ce que les utilisateurs de votre classe d’origine sont supposés travailler, et à quoi les classes dérivées devraient ressembler, vous aussi. Afin de fournir une encapsulation correcte, il ne s’agit généralement que de méthodes (et de classes / structures d’aide, enum, typedefs, quels que soient les besoins de l’utilisateur pour travailler avec vos méthodes), et non de variables.
  • déclarez les méthodes protégées qui pourraient être utiles à quelqu'un qui souhaite étendre / spécialiser les fonctionnalités de votre classe, mais ne devrait pas faire partie de l' interface publique - en fait, vous augmentez généralement les membres privés à protected en cas de besoin. En cas de doute, jusqu'à ce que vous sachiez que
    1. votre classe peut / peut / sera sous-classée,
    2. et avoir une idée claire de ce que peuvent être les cas d'utilisation du sous-classement.

Et vous ne vous écartez de ce schéma général que s'il existe une bonne raison ™ . Méfiez-vous "cela me facilitera la vie plus facilement quand je pourrai y accéder librement de l'extérieur" (et l' extérieur comprend également des sous-classes). Lorsque j'implémente des hiérarchies de classes, je commence souvent par des classes sans membres protégés, jusqu'à ce que je parvienne à les sous-classer / développer / les spécialiser, à devenir les classes de base d'un framework / toolkit et à déplacer parfois une partie de leur fonctionnalité d'origine d'un niveau supérieur.

Murphy
la source
1

Une question plus intéressante, peut-être, est de savoir pourquoi tout type de domaine autre que privé est nécessaire. Lorsqu'une sous-classe a besoin d'interagir avec les données d'une super-classe, cela crée directement un couplage direct entre les deux, alors que l'utilisation de méthodes pour assurer l'interaction entre les deux permet un niveau d'indirection permettant de modifier le superclasse qui serait autrement très difficile.

Un certain nombre de langages (par exemple, Ruby et Smalltalk) ne fournissent pas de champs publics, ce qui dissuadera les développeurs d’autoriser le couplage direct avec leurs implémentations de classes. Pourquoi ne pas aller plus loin et n’avoir que des champs privés? Il n'y aurait aucune perte de généralité (car la super-classe peut toujours fournir des accesseurs protégés pour la sous-classe), mais cela garantirait que les classes ont toujours au moins un faible degré d'isolement par rapport à leurs sous-classes. Pourquoi n'est-ce pas une conception plus commune?

Jules
la source
1

Beaucoup de bonnes réponses ici, mais je vais ajouter mes deux cents de toute façon. :-)

Privé est bon pour la même raison que les données globales sont mauvaises.

Si une classe déclare des données privées, vous savez alors que le seul code qui perturbe ces données est le code de la classe. Quand il y a un bogue, vous n'avez pas besoin de chercher partout dans la création pour trouver tous les endroits susceptibles de modifier ces données. Vous savez que c'est dans la classe. Lorsque vous apportez une modification au code et que vous modifiez quelque chose sur l'utilisation de ce champ, vous n'êtes pas obligé de rechercher tous les emplacements potentiels susceptibles d'utiliser ce champ et d'étudier si votre modification planifiée les supprimera. Vous savez que les seuls endroits sont à l'intérieur de la classe.

De nombreuses fois, j'ai dû modifier des classes qui se trouvent dans une bibliothèque et qui sont utilisées par plusieurs applications, et je dois agir avec précaution pour m'assurer de ne pas endommager une application dont je ne connais rien. Plus il y a de données publiques et protégées, plus il y a de risque de problèmes.

Geai
la source
1

Je pense qu'il vaut la peine de mentionner certaines opinions divergentes.

En théorie, il est bon d'avoir un niveau d'accès contrôlé pour toutes les raisons mentionnées dans les autres réponses.

Dans la pratique, trop souvent lors de la révision de code, je vois des personnes (qui aiment utiliser des ressources privées) qui changent le niveau d'accès de privé -> protégé et pas trop souvent de protégé -> public. Presque toujours, la modification des propriétés de classe implique la modification de setters / getters. Celles-ci ont perdu une grande partie de mon temps (révision du code) et de la leur (changement de code).

Cela m'agace également que cela signifie que leurs classes ne sont pas fermées pour modification.

C'était avec le code interne où vous pouvez toujours le changer si vous en avez besoin aussi. La situation est pire avec du code tiers lorsqu'il n'est pas si facile de changer de code.

Alors, combien de programmeurs pensent que c'est un problème? Eh bien, combien utilisent des langages de programmation qui n'ont pas privé? Bien sûr, les gens n'utilisent pas seulement ces langues parce qu'ils n'ont pas de spécificateurs privés, mais cela simplifie les langages et la simplicité est importante.

Imo, c'est très similaire au typage dynamique / statique. En théorie, le typage statique est très bon. Dans la pratique, il empêche que comme 2% d'erreurs L'efficacité de dynamique frappe Unreasonable ... . L'utilisation de private empêche probablement moins d'erreur que cela.

Je pense que les principes SOLID sont bons, je souhaite que les gens s’intéressent davantage à eux que de créer une classe publique, protégée et privée.

imel96
la source
1
Si vous devez modifier le code tiers lorsque vous l'utilisez, il est soit mal conçu, soit vous ne le réutilisez pas bien. Vous pouvez toujours réutiliser une classe non abstraite en l'encapsulant, mais vous n'avez pratiquement jamais besoin de la sous-classer.
Petit Santi
0

J'aimerais également ajouter un autre exemple concret de pourquoi, cela protectedne suffit pas. Les premières années de mon université, je me suis lancée dans un projet qui consistait à développer une version de bureau d'un jeu de plateau (pour lequel une intelligence artificielle est ensuite développée et connectée à d'autres joueurs via un réseau). Un code partiel leur est fourni pour leur permettre de commencer, y compris un cadre de test. Certaines propriétés de la classe de jeu principale sont exposées de protectedmanière à ce que les classes de test qui étendent cette classe y aient accès. Mais ces champs ne sont pas des informations sensibles.

En tant qu'assistant-tuteur pour l'unité, je vois souvent les étudiants faire simplement tout leur code ajouté protectedou public(peut-être parce qu'ils ont vu l'autre protectedet d' autres publicchoses et ont supposé qu'ils devaient suivre). Je leur demande pourquoi leur niveau de protection est inapproprié et beaucoup ne savent pas pourquoi. La réponse est que les informations sensibles qu’ils exposent à des sous-classes signifient qu’un autre joueur peut tricher en élargissant simplement cette classe et en accédant aux informations très sensibles du jeu (essentiellement la position cachée de l’adversaire, ce qui serait semblable à ce que ce serait si pourrait voir vos adversaires en morceaux sur un tableau de cuirassés en prolongeant une classe). Cela rend leur code très dangereux dans le contexte du jeu.

En dehors de cela, il existe de nombreuses autres raisons de garder quelque chose de privé même dans vos sous-classes. C’est peut-être pour cacher des détails d’implémentation qui pourraient gâcher le bon fonctionnement de la classe s’ils étaient modifiés par une personne qui ne sait pas nécessairement ce qu’ils font (principalement en pensant à d’autres personnes utilisant votre code ici).

J_mie6
la source
1
Je ne comprends pas. À moins que les joueurs ne prolongent leurs classes de manière dynamique pendant que le jeu est en cours, comment cela pourrait-il être utilisé pour tricher? Cela impliquerait que vous importiez du code non fiable dans votre base de code. Si vous dites que vous donnez aux étudiants des classes compilées et les empêchez de tricher dans leur tâche en modifiant les détails de la mise en oeuvre, la définition d'un niveau de protection ne rendra pas la tâche plus sûre. Les étudiants peuvent décompiler des bibliothèques ou utiliser la réflexion à leur avantage. Cela dépend bien sûr du niveau d'application que vous recherchez.
Sam
@sam Généralement, le code doit être écrit avec l'hypothèse que celui qui le modifiera ensuite n'aura pas besoin de connaître l'intégralité de la base de code. Cela pourrait même être l'auteur original parfois (le temps passe, le manque de sommeil ...). La tricherie peut consister à donner aux élèves un code squelette à développer et à modifier la logique qu’ils ne devraient pas toucher.
Phil Lello
0

Les méthodes / variables privées seront généralement cachées d'une sous-classe. Cela peut être une bonne chose.

Une méthode privée peut émettre des hypothèses sur des paramètres et laisser la vérification de l'intégrité à l'appelant.

Une méthode protégée devrait vérifier les entrées.

Phil Lello
la source
-1

"privé" signifie: non destiné à être modifié ou accessible par quiconque à l'exception de la classe elle-même. Non destiné à être modifié ou accessible par les sous-classes. Sous-classes? Quelles sous-classes? Vous n'êtes pas censé sous-classer cela!

"protégé" signifie: uniquement destiné à être modifié ou accessible par classes ou sous-classes. Déduction probable que vous êtes censé sous-classer, sinon pourquoi "protégé" et non "privé"?

Il y a une nette différence ici. Si je fais quelque chose de privé, vous êtes censé garder vos doigts sales hors de lui. Même si vous êtes une sous-classe.

gnasher729
la source
-1

Une des premières choses que je fais quand je sous-classe une classe est de changer un tas de méthodes privées en protégées

Quelques raisonnements sur les méthodesprivate vs :protected

privateméthodes empêchent la réutilisation du code. Une sous-classe ne peut pas utiliser le code dans la méthode privée et peut être amenée à le réimplémenter - ou à réimplémenter la ou les méthodes qui dépendent à l'origine de la méthode privée & c.

Par ailleurs, toute méthode qui ne le serait pasprivate peut être vue comme une API fournie par "" au monde extérieur ", en ce sens que les sous-classes tierces sont également considérées comme" monde extérieur ", comme quelqu'un d'autre l'a suggéré dans sa réponse. déjà.

Est-ce une mauvaise chose? - Je ne pense pas.

Bien sûr, une API (pseudo-) publique verrouille le programmeur d'origine et empêche le refactoring de ces interfaces. Mais à l'inverse, pourquoi un programmeur ne devrait-il pas concevoir ses propres "détails d'implémentation" d'une manière aussi propre et stable que son API publique? Devrait-il utiliser privatepour qu'il puisse être négligent dans la structuration de son code "privé"? Pensant peut-être qu'il pourrait le nettoyer plus tard, parce que personne ne le remarquera? - Non.

Le programmeur devrait également réfléchir un peu à son code "privé" afin de le structurer de manière à permettre ou même de promouvoir la réutilisation de la plus grande partie possible. Ensuite, les parties non privées risquent de ne plus devenir un fardeau dans le futur que certains le craignent.

Beaucoup de code (cadre) Je vois adopte une utilisation incohérente de private: protected, les méthodes non-finales qui ne peine quelque chose de plus que de déléguer à une méthode privée trouve couramment. protected, méthodes non finales dont le contrat ne peut être rempli qu’avec un accès direct à des champs privés.

Ces méthodes ne peuvent pas être logiquement surchargées / améliorées, bien que techniquement, rien ne la rende évidente (pour le compilateur).

Vous voulez l'extensibilité et l'héritage? Ne faites pas vos méthodes private.

Vous ne voulez pas que certains comportements de votre classe soient modifiés? Faites vos méthodes final.

Vous ne pouvez vraiment pas appeler votre méthode en dehors d’un certain contexte bien défini? Rendez votre méthode privateet / ou réfléchissez à la manière dont vous pouvez rendre le contexte bien défini requis disponible pour une réutilisation via une autre protectedméthode d'encapsulation.

C'est pourquoi je préconise une utilisation privatemodérée. Et à ne pas confondre privateavec final. - Si la mise en œuvre d'une méthode est vitale pour le contrat général de la classe et qu'elle ne doit donc pas être remplacée / annulée, faites-la final!

Pour les champs, ce privaten'est pas vraiment mauvais. Tant que le (s) champ (s) peuvent être raisonnablement "utilisés" via des méthodes appropriées (ce n'est pas getXX() ou setXX()!).

JimmyB
la source
2
-1 Cela ne répond pas à la question d'origine privatevs protected, donne des conseils erronés ("utiliser privateavec parcimonie"?) Et va à l'encontre du consensus général sur la conception de OO. L'utilisation privaten'a rien à voir avec le masquage de code bâclé.
Andres F.
3
Vous dites qu'une classe a une API, mais ne semble pas établir de connexion que l'utilisateur de l'API ne veut pas être surchargé de détails qu'il n'a pas besoin de connaître. La situation idéale pour l'utilisateur d'une classe est que l'interface ne contienne que les méthodes dont elle a besoin, et rien d'autre. La création de méthodes privatepermet de garder l'API propre.
Ben Cottrell
1
@HannoBinder Je conviens qu'il y a beaucoup de classes qui souffrent de rigidité, mais la flexibilité et la réutilisation des codes sont beaucoup moins importantes que l'encapsulation et la maintenabilité à long terme. Considérez votre cas où toutes les classes ont des membres protégés et que les classes dérivées peuvent gâcher tous les détails d’implémentation qu’ils souhaitent; Comment pouvez-vous tester de manière fiable ces classes en sachant que quoi que ce soit dans le code non encore écrit pourrait entraîner l'échec de ce code à tout moment? combien de "hacks" de ce genre le code pourrait-il prendre avant que tout ne dégénère en une grosse boule de boue?
Ben Cottrell
2
Oh, vous avez dit "membres". Ce que je dis ne concerne que les méthodes . Les variables membres (champs) sont une histoire différente, où la vie privée est beaucoup plus justifiée.
JimmyB
2
La nomenclature dans la discussion est omniprésente car elle n’est pas spécifique à une langue en particulier. "Membre" en C ++ peut être données, fonction, type ou modèle.
JDługosz
-1

Connaissez-vous un cas d’usage important où privé et non protégé est un bon outil, ou deux options «protégé et public» seraient-elles suffisantes pour les langues POO?

Privé : lorsque vous avez quelque chose qui ne sera jamais utile pour aucune sous-classe à appeler ou à remplacer.

Protégé : lorsque vous avez quelque chose qui a une implémentation / constante spécifique à une sous-classe.

Un exemple:

public abstract Class MercedesBenz() extends Car {
  //Might be useful for subclasses to know about their customers
  protected Customer customer; 

  /* Each specific model has its own horn. 
     Therefore: protected, so that each subclass might implement it as they wish
  */
  protected abstract void honk();

  /* Taken from the car class. */
  @Override
  public void getTechSupport(){
     showMercedesBenzHQContactDetails(customer);
     automaticallyNotifyLocalDealer(customer);
  }

  /* 
     This isn't specific for any subclass.
     It is also not useful to call this from inside a subclass,
     because local dealers only want to be notified when a 
     customer wants tech support. 
   */
  private void automaticallyNotifyLocalDealer(){
    ...
  }
}
Arnab Datta
la source
2
lorsque vous avez quelque chose qui ne sera jamais utile à une sous-classe d'appeler ou de passer outre - Et c'est quelque chose que vous, à mon avis, ne savez jamais à l'avance.
Adam Libuša
Eh bien, je pourrais aussi avoir besoin d’appuyer sur le bouton de réinitialisation de ma CMOS. Mais l'hypothèse par défaut est que je n'en aurai pas vraiment besoin et que, par conséquent, il est placé à l'intérieur du châssis. La même chose vaut pour les méthodes. Vous le protégez si la sous-classe a absolument besoin de la réimplémenter / de l'appeler (voir: klaxon). Vous le rendez public si d'autres parties doivent l'appeler.
Arnab Datta
-2

Il était difficile pour moi de comprendre cette question, alors j'aimerais partager un extrait de mon expérience:

  • Quel est le champ protégé ? Il est rien de plus qu'un champ, qui ne peut être accessible en dehors d' une classe, à savoir publiquement comme ceci: $classInstance->field. Et le truc c'est "ça y est". Les enfants de votre classe y auront pleinement accès, car c'est leur partie interne légitime.
  • Quel est le domaine privé ? C'est un " vrai privé " pour votre propre classe et votre propre implémentation de cette classe. "Tenir hors de portée des enfants", comme sur un flacon de médicament. Vous aurez la garantie que c'est irréversible par les dérivés de votre classe, vos méthodes - quand elles sont appelées - auront exactement ce que vous avez déclaré

UPDATE: un exemple pratique d'une tâche réelle que j'ai résolue. La voici: vous avez un jeton, comme USB ou LPT (c'était mon cas), et vous avez un middleware. Le jeton vous demande un code PIN, s'ouvre s'il est correct et vous pouvez envoyer une partie chiffrée et un certain nombre de clés à déchiffrer. Les clés sont stockées dans un jeton, vous ne pouvez pas les lire, seulement les utiliser. Et il y avait des clés temporaires pour une session, signées par une clé dans un jeton, mais stockées dans un middleware lui-même. La clé temporaire n'était pas censée fuir ailleurs, mais pour exister au niveau du pilote. Et j'ai utilisé un champ privé pour stocker cette clé temporaire et certaines données relatives à la connexion matérielle. Donc , pas de dérivés ont pu utiliser non seulement une interface publique, mais aussi une certaine protectionLes sous-routines "pratiques" que j'ai créées pour une tâche, mais ne parvenaient pas à ouvrir un coffre-fort avec les clés et l'interaction matérielle. Logique?

Alexey Vesnin
la source
Désolé les gars! juste foiré - mettre à jour ma réponse =) MERCI! J'étais pressé et mal typé =)
Alexey Vesnin
2
Je pense que le PO est conscient des différences entre les deux niveaux de protection, mais cherche à savoir pourquoi l'un devrait être utilisé plutôt que l'autre.
Sam
@ Sam s'il vous plaît prendre une lecture plus profonde de ma réponse. Ce sont des types de champs complètement différents ! La seule chose qu'ils ont en commun est qu'ils ne peuvent pas être référencés publiquement
Alexey Vesnin
2
Je ne sais pas de quoi vous parlez. Je suis conscient des différences entre privé et protégé. Peut-être avez-vous mal compris ce que je voulais dire par niveau de protection? Je parle de 'niveau d'accès. J'essayais de souligner que même si votre réponse n'est pas mauvaise, elle ne répond pas directement à la question. Le PO semble savoir ce que signifient à la fois protégé / privé / public, mais ne sait pas dans quelles circonstances il voudrait choisir l'un par rapport à l'autre. C'est peut-être comme ça que vous avez été rejeté (pas par moi).
Sam
@Sam Je pense avoir compris votre argument - ajouté un cas pratique issu de ma pratique / expérience réelle. Est-ce ce qui manque sur votre point?
Alexey Vesnin