Privé vs protégé - Préoccupation relative aux bonnes pratiques en matière de visibilité [fermé]

145

J'ai cherché et je connais la différence théorique.

  • public - Toute classe / fonction peut accéder à la méthode / propriété.
  • protected - Seule cette classe et toutes les sous-classes peuvent accéder à la méthode / propriété.
  • private - Seule cette classe peut accéder à la méthode / propriété. Il ne sera même pas hérité.

C'est très bien, la question est de savoir quelle est la différence pratique entre eux? Quand utiliseriez-vous privateet quand utiliseriez-vous protected? Existe-t-il une bonne pratique standard ou acceptable par rapport à celle-ci?

Jusqu'à présent, pour conserver le concept d'héritage et de polymorphisme, j'utilise publicpour tout ce qui doit être accédé de l'extérieur (comme les constructeurs et les fonctionnalités de classe principale), et protectedpour les méthodes internes (logique, méthodes d'assistance, etc.). Suis-je sur la bonne voie?

(Notez que cette question est pour moi, mais aussi pour référence future car je n'ai pas vu de question comme celle-ci SO).

Fantôme de Madara
la source
33
Est-ce que ça importe? Toute langue avec prise en charge de la POO a ce problème. Il se trouve que je programme en PHP, mais je pense que la question s'applique à tout langage de support OOP.
Madara's Ghost
2
D'accord, je me demande simplement si vous avez oublié de marquer. Maintenant, je vois la balise oop.
Paul Bellora
Je dois définir la visibilité (privée / publique / protégée) pour chaque propriété de la classe? Ou seuls certains d'entre eux doivent avoir un type de visibilité? Si oui, comment décider quelle propriété doit avoir une visibilité définie en haut de la classe?
user4271704
1
désinvolte, la différence entre protégé et privé semble évidente. Utilisez protected si les sous-classes utilisent la méthode / variable, sinon utilisez private. Plus précisément, si des sous-classes doivent redéfinir une variable privée très similaire dans le parent, il suffit de la protéger.
iPherian
Il existe un autre spécificateur d'accès internalen langage C # qui limite le niveau d'accès d'une classe ou de ses membres au sein d'un assembly physique. Cependant, je ne suis pas sûr de son support ou de quelque chose de similaire dans d'autres langues.
RBT

Réponses:

128

Non, vous n'êtes pas sur la bonne voie. Une bonne règle de base est la suivante: rendre tout aussi privé que possible. Cela rend votre classe plus encapsulée et permet de modifier les éléments internes de la classe sans affecter le code en utilisant votre classe.

Si vous concevez votre classe pour qu'elle soit héritable, choisissez avec soin ce qui peut être remplacé et accessible à partir des sous-classes, et rendez-le protégé (et finalement, en parlant de Java, si vous voulez le rendre accessible mais non remplaçable). Mais sachez que, dès que vous acceptez d'avoir des sous-classes de votre classe, et qu'il existe un champ ou une méthode protégé, ce champ ou cette méthode fait partie de l'API publique de la classe, et ne peut pas être modifié ultérieurement sans casser les sous-classes.

Une classe qui n'est pas destinée à être héritée doit être rendue définitive (en Java). Vous pouvez assouplir certaines règles d'accès (privé à protégé, final à non final) pour des raisons de test unitaire, mais ensuite les documenter et indiquer clairement que bien que la méthode soit protégée, elle n'est pas censée être remplacée.

JB Nizet
la source
1
La vraie question ici, est à propos de privatevs protectedQuand est-ce que je veux qu'une propriété soit héritée, et quand n'est-ce pas? Je ne peux pas vraiment dire si un utilisateur veut parfois à l'avenir suivre mon cours et le prolonger ...
Madara's Ghost
16
Eh bien, la question n'est pas de savoir ce que l'utilisateur veut remplacer, la question est de savoir ce que vous voulez autoriser à être remplacé. Habituellement, cela aide à changer de camp et à essayer de penser: si j'utilisais cette classe et créais une sous-classe, que voudrais-je pouvoir remplacer? Parfois, d'autres utilisateurs manqueront encore des choses ...
Thorsten Dittmar
3
Les champs protégés sont une mauvaise pratique en matière d'héritage! . Veuillez en prendre garde. Voici pourquoi
RBT
4
Les champs privés sont mauvais en pratique en héritage !. (exagérant) Veuillez lire ma réponse.
Larry
6
Opinion typique du maniaque du contrôle.
bruno desthuilliers
58

Permettez-moi de commencer en disant que je parle ici principalement de l'accès aux méthodes et, dans une moindre mesure, du marquage des classes comme finales, et non de l' accès des membres.

La vieille sagesse

»

avait du sens à l'époque où il a été écrit, avant que l'open source ne domine l'espace des bibliothèques de développeurs et la gestion des VCS / dépendances. est devenu hyper collaboratif grâce à Github, Maven, etc. À l'époque, il y avait aussi de l'argent à gagner en limitant la ou les façons dont une bibliothèque pouvait être utilisée. J'ai passé probablement les 8 ou 9 premières années de ma carrière à adhérer strictement à cette «meilleure pratique».

Aujourd'hui, je pense que c'est un mauvais conseil. Parfois, il y a un argument raisonnable pour marquer une méthode comme privée ou une classe finale, mais c'est extrêmement rare, et même dans ce cas, cela n'améliore probablement rien.

As-tu déjà:

  • Vous avez été déçu, surpris ou blessé par une bibliothèque, etc. qui avait un bogue qui aurait pu être corrigé avec l'héritage et quelques lignes de code, mais en raison de méthodes privées / finales et les classes ont été obligées d'attendre un correctif officiel qui pourrait ne jamais venir? J'ai.
  • Vous vouliez utiliser une bibliothèque pour un cas d'utilisation légèrement différent de celui imaginé par les auteurs, mais vous n'avez pas pu le faire en raison de méthodes et de classes privées / finales? J'ai.
  • Vous avez été déçu, surpris ou blessé par une bibliothèque, etc. trop permissive dans son extensibilité? Je n'ai pas.

Voici les trois plus grandes rationalisations que j'ai entendues pour le marquage des méthodes privées par défaut:

Rationalisation n ° 1: ce n'est pas sûr et il n'y a aucune raison de remplacer une méthode spécifique

Je ne peux pas compter le nombre de fois où je me suis trompé sur la nécessité ou non de remplacer une méthode spécifique que j'ai écrite. Ayant travaillé sur plusieurs bibliothèques open source populaires, j'ai appris à mes dépens le coût réel du marquage des choses privées. Il élimine souvent la seule solution pratique aux problèmes imprévus ou aux cas d'utilisation. À l'inverse, je n'ai jamais, en plus de 16 ans de développement professionnel, regretté d'avoir marqué une méthode protégée au lieu de privée pour des raisons liées à la sécurité des API. Lorsqu'un développeur choisit d'étendre une classe et de remplacer une méthode, il dit consciemment «Je sais ce que je fais». et pour des raisons de productivité, cela devrait suffire. période. Si c'est dangereux, notez-le dans la classe / méthode Javadocs, ne vous contentez pas de claquer aveuglément la porte.

Les méthodes de marquage protégées par défaut sont une atténuation pour l'un des problèmes majeurs du développement de logiciels modernes: l'échec de l'imagination.

Rationalisation n ° 2: elle garde les API / Javadocs publiques propres

Celui-ci est plus raisonnable, et en fonction du public cible, cela peut même être la bonne chose à faire, mais cela vaut la peine de considérer le coût de maintien de l'API "propre": l'extensibilité. Pour les raisons mentionnées ci-dessus, il est probablement plus judicieux de marquer les éléments protégés par défaut au cas où.

Rationalisation n ° 3: Mon logiciel est commercial et je dois limiter son utilisation.

C'est également raisonnable, mais en tant que consommateur, j'irais avec le concurrent le moins restrictif (en supposant qu'il n'y ait pas de différences de qualité significatives) à chaque fois.

Ne jamais dire jamais

Je ne dis pas de ne jamais marquer les méthodes comme privées. Je dis que la meilleure règle de base est de "rendre les méthodes protégées à moins qu'il n'y ait une bonne raison de ne pas le faire".

Ce conseil est le mieux adapté pour ceux qui travaillent sur des bibliothèques ou des projets à plus grande échelle qui ont été divisés en modules. Pour les projets plus petits ou plus monolithiques, cela n'a pas autant d'importance car vous contrôlez tout le code de toute façon et il est facile de changer le niveau d'accès de votre code si / quand vous en avez besoin. Même dans ce cas, je donnerais toujours le même conseil :-)

pseudo
la source
Wrt votre Rationalization # 1- Lorsque je marque quelque chose comme protégé, je choisis de le faire explicitement car je pense que ce comportement n'est pas définitif et pourrait être un cas où mes classes enfants voudraient le remplacer. Si je ne suis pas si sûr, je voudrais le rendre privé par défaut. Surtout dans un langage comme C # qui conserve une méthode non virtuelle par défaut, l'ajout d'un spécificateur d'accès uniquement protégé n'a aucun sens. Vous devez ajouter à la fois des mots virtual- protectedclés et pour rendre votre intention très claire. De plus, les gens préfèrent l'association à l'héritage, de sorte protectedque la valeur par défaut est difficile à percevoir
RBT
4
La combinaison de mots-clés nécessaires pour rendre une méthode remplaçable est un détail de langage IMHO. Le contre-argument que je présente à la rationalisation n ° 1 vise carrément le cas que vous mentionnez "Si je ne suis pas si sûr ...". En pratique, si vous ne pouvez pas penser à une raison pour laquelle cela serait dangereux, il y a plus à gagner en optant pour l'extensibilité. Quand vous dites «association», je prends cela pour un synonyme de composition, auquel cas je ne vois pas que cela importe de toute façon.
Nick
3
Je ne me souviens pas combien de fois j'ai dû "cloner" les classes sous-jacentes juste parce que je voulais remplacer 1 ou 2 ou les méthodes. Et comme ce que vous avez dit, avec la tendance sociale selon laquelle nous partageons plus de code que jamais auparavant, il est beaucoup plus logique d'utiliser par défaut protégé que privé. c'est-à-dire être plus ouvert à vos propres logiques.
shinkou
1
Se mettre d'accord. Je voulais juste modifier BufferedReader de Java pour gérer les délimiteurs de ligne personnalisés. Grâce aux champs privés, je dois cloner toute la classe plutôt que de simplement l'étendre et de remplacer readLine ().
Ron Dunn
21

Arrêtez d'abuser des champs privés !!!

Les commentaires ici semblent être extrêmement favorables à l'utilisation de champs privés. Eh bien, j'ai quelque chose de différent à dire.

Les champs privés sont-ils bons en principe? Oui. Mais dire qu'une règle d'or est de rendre tout privé lorsque vous n'êtes pas sûr que ce soit vraiment faux ! Vous ne verrez pas le problème jusqu'à ce que vous en rencontriez un. À mon avis, vous devriez marquer les champs comme protégés si vous n'êtes pas sûr .

Il existe deux cas pour lesquels vous souhaitez étendre une classe:

  • Vous souhaitez ajouter des fonctionnalités supplémentaires à une classe de base
  • Vous souhaitez modifier la classe existante qui se trouve en dehors du package actuel (dans certaines bibliothèques peut-être)

Il n'y a rien de mal avec les champs privés dans le premier cas. Le fait que les gens abusent des champs privés rend très frustrant lorsque vous découvrez que vous ne pouvez pas modifier la merde.

Considérez une bibliothèque simple qui modélise des voitures:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

L'auteur de la bibliothèque a pensé: il n'y a aucune raison pour laquelle les utilisateurs de ma bibliothèque ont besoin d'accéder aux détails d'implémentation de assembleCar()droite? Marquons la vis comme privée.

Eh bien, l'auteur a tort. Si vous souhaitez modifier uniquement la assembleCar()méthode sans copier toute la classe dans votre package, vous n'avez pas de chance. Vous devez réécrire votre propre screwchamp. Disons que cette voiture utilise une douzaine de vis, et chacune d'elles implique un code d'initialisation non trivial dans différentes méthodes privées, et ces vis sont toutes marquées comme privées. À ce stade, ça commence à sucer.

Oui, vous pouvez dire avec moi que l'auteur de la bibliothèque aurait pu écrire un meilleur code donc il n'y a rien de mal avec les champs privés . Je ne dis pas que le champ privé est un problème avec la POO . C'est un problème lorsque les gens les utilisent.

La morale de l'histoire est que si vous écrivez une bibliothèque, vous ne savez jamais si vos utilisateurs veulent accéder à un domaine particulier. Si vous n'êtes pas sûr, marquez-le protectedpour que tout le monde soit plus heureux plus tard. Au moins, n'abusez pas du domaine privé .

Je soutiens beaucoup la réponse de Nick.

Larry
la source
2
Utiliser des champs privés signifie principalement que l'héritage est la mauvaise abstraction pour modifier un certain comportement d'une classe / objet (s'il est utilisé consciemment). La modification violera généralement silencieusement LSP car le comportement est modifié sans que l'API change. Excellente réponse, +1.
Madara's Ghost
Prêcher! Le privé est le fléau de mon existence lorsque j'essaie d'écrire des mods Minecraft. Rien de plus ennuyeux que de devoir utiliser Reflection ou ASM pour résoudre ce problème.
RecursiveExceptionException
Comme j'ai compris la réponse de Nick, il faisait surtout référence aux méthodes et non aux propriétés. Je suis tout à fait d'accord que nous ne devrions pas déclarer les méthodes privées à chaque fois et que leur utilisation doit être réfléchie, mais pourquoi conseilleriez-vous de déclarer les propriétés protégées plutôt que privées simplement parce que vous souhaitez "réécrire" plus facilement la classe. Partagez totalement votre frustration alors que je travaille quotidiennement avec les 3rd p, mais encourageons les gens à créer une architecture solide et non pas simplement en disant "le code finirait par être de la merde, alors protégeons-le dès le début afin que nous puissions le réécrire facilement". Bien que je partage votre frustration.
sean662 le
16

Il y a quelque temps, j'ai lu un article qui parlait de verrouiller au maximum chaque classe. Rendez tout final et privé, sauf si vous avez un besoin immédiat d'exposer certaines données ou fonctionnalités au monde extérieur. Il est toujours facile d'étendre la portée pour qu'elle soit plus autorisée plus tard, mais pas l'inverse. Tout d' abord envisager de faire autant de choses que possible finalqui fera le choix entre privateet protectedbeaucoup plus facile.

  1. Rendez toutes les classes définitives à moins que vous n'ayez besoin de les sous-classer immédiatement.
  2. Rendez toutes les méthodes définitives à moins que vous n'ayez besoin de les sous-classes et de les remplacer immédiatement.
  3. Rendez tous les paramètres de méthode définitifs à moins que vous n'ayez besoin de les modifier dans le corps de la méthode, ce qui est un peu gênant la plupart du temps de toute façon.

Maintenant, s'il vous reste une classe finale, rendez tout privé à moins que le monde n'en ait absolument besoin - rendez-le public.

S'il vous reste une classe qui a des sous-classes, examinez attentivement chaque propriété et méthode. Considérez d'abord si vous souhaitez même exposer cette propriété / méthode à des sous-classes. Si vous le faites, déterminez si une sous-classe peut faire des ravages sur votre objet si elle a gâché la valeur de la propriété ou l'implémentation de la méthode lors du processus de substitution. Si c'est possible, et que vous voulez protéger la propriété / méthode de votre classe même des sous-classes (cela semble ironique, je sais), alors rendez-la privée. Sinon, protégez-le.

Avertissement: je ne programme pas beaucoup en Java :)

Anurag
la source
2
Pour clarifier: A final classen Java est une classe dont on ne peut pas hériter. A final methodn'est pas virtuel, c'est-à-dire qu'il ne peut pas être remplacé par une sous-classe (les méthodes Java sont virtuelles par défaut). A final field/parameter/variableest une référence de variable immuable (dans le sens où elle ne peut pas être modifiée après avoir été initialisée).
M.Stramm
1
Curieusement, j'ai vu le conseil exact opposé, que rien ne devrait être définitif à moins qu'il ne soit certain que le dépassement de la chose en question a le potentiel de briser un invariant. De cette façon, tout le monde est libre d'étendre vos cours au besoin.
GordonM
Pouvez-vous citer un cas dans votre carrière de programmeur où le fait de marquer un paramètre de méthode «final» a fait une différence dans votre compréhension? (sauf si requis par le compilateur)
user949300
7

Quand utiliseriez-vous privateet quand utiliseriez-vous protected?

L'héritage privé peut être considéré comme mis en œuvre en termes de relation plutôt que de relation IS-A . En termes simples, l'interface externe de la classe héritière n'a aucune relation (visible) avec la classe héritée. Elle n'utilise l' privatehéritage que pour implémenter une fonctionnalité similaire fournie par la classe Base.

Contrairement à l'héritage privé, l' héritage protégé est une forme restreinte d'héritage, dans laquelle la classe dérivée IS-A est une sorte de classe de base et souhaite restreindre l'accès des membres dérivés uniquement à la classe dérivée.

Alok Save
la source
2
Cette «mise en œuvre en termes de relation» est une relation HAS-A. Si tous les attributs sont privés, le seul moyen d'y accéder est via les méthodes. C'est la même chose que si la sous-classe contenait un objet de la super-classe. Éliminer tous les attributs protégés de vos classes concrètes signifie également éliminer tout héritage de celles-ci.
shawnhcorey
2
Processus de réflexion intéressant - Héritage privé . @shawnhcorey merci d'avoir précisé qu'il pointe vers une relation HAS-A aka association (qui peut être une agrégation ou une composition). Je pense que cet article devrait également être mis à jour pour clarifier la même chose.
RBT
1

Eh bien, tout est question d'encapsulation si les classes de factures de paiement gèrent la facturation du paiement, puis dans la classe de produit, pourquoi aurait-il besoin de tout le processus de facturation, c'est-à-dire du mode de paiement comment payer où payer ... donc ne laisser que ce qui est utilisé pour d'autres classes et objets rien de plus que ce public pour ceux que d'autres classes utiliseraient aussi, protégé pour ces limites uniquement pour l'extension des classes. Comme vous êtes madara uchiha, le privé est comme "limboo"vous pouvez le voir (vous ne classez qu'une seule classe).

ujwal dhakal
la source