154 votes

Private vs Protégé - Bonne pratique de visibilité

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

  • public - N'importe quelle classe/fonction peut accéder à la méthode/propriété.
  • protected - Seule cette classe et ses sous-classes peuvent accéder à la méthode/propriété.
  • private - Seule cette classe peut accéder à la méthode/propriété. Elle ne sera même pas héritée.

Tout cela va bien, mais la question est, quelle est la différence pratique entre eux ? Quand utiliseriez-vous private et quand utiliseriez-vous protected ? Existe-t-il une pratique standard ou acceptable à ce sujet ?

Jusqu'à présent, pour conserver le concept d'héritage et de polymorphisme, j'utilise public pour tout ce qui doit être accessible depuis l'extérieur (comme les constructeurs et la fonctionnalité principale de la classe), et protected pour les méthodes internes (logique, méthodes d'aide, 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 sur Stack Overflow).

34 votes

Est-ce que cela importe? Toute langue avec un support OOP a cette préoccupation. Je programme en PHP, mais je pense que la question s'applique à n'importe quelle langue supportant l'OOP.

2 votes

D'accord, juste pour savoir si vous aviez oublié de taguer. Maintenant je vois la balise oop.

0 votes

Je dois définir la visibilité (privée/publique/protégée) pour chaque propriété de la classe? Ou seulement certaines ont besoin d'avoir un type de visibilité? Si oui, comment décider quelle propriété doit avoir une visibilité définie en haut de la classe?

140voto

JB Nizet Points 250258

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

Si vous concevez votre classe pour qu'elle soit héritée, choisissez soigneusement ce qui peut être remplacé et accessible à partir des sous-classes, et rendez-le protégé (et final, 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 y a 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 finale (en Java). Vous pouvez assouplir certaines règles d'accès (de privé à protégé, de final à non-final) pour les besoins des tests unitaires, mais documentez-le, et précisez que même si la méthode est protégée, elle ne doit pas être remplacée.

1 votes

La vraie question ici, concerne private vs protected Quand est-ce que je veux qu'une propriété soit héritée, et quand est-ce que je ne le veux pas? Je ne peux pas vraiment dire si un utilisateur voudra éventuellement prendre ma classe et l'étendre...

16 votes

Eh bien, la question n'est pas ce que l'utilisateur souhaite remplacer, la question est ce que vous voulez autoriser à être remplacé. Il est généralement utile de changer de perspective et d'essayer de réfléchir : Si j'ai utilisé cette classe et j'ai créé une sous-classe, qu'est-ce que je voudrais pouvoir remplacer ? Parfois, d'autres utilisateurs pourront toujours passer à côté de certaines choses...

0 votes

Après avoir relu votre réponse, j'ai compris. En fait, je ne savais pas que PHP avait final jusqu'à récemment. Merci.

73voto

Nick Points 3318

Permettez-moi de préciser que je parle principalement de l'accès aux méthodes ici, et dans une moindre mesure, de marquer les classes finales, pas l'accès aux membres.

L'ancienne sagesse

"marquez-le privé à moins d'avoir une bonne raison de ne pas le faire"

avait du sens à l'époque où elle a été écrite, avant que l'open source ne domine l'espace des bibliothèques de développeurs et que la gestion des dépendances devienne hyper-collaborative grâce à Github, Maven, etc. À l'époque, il y avait aussi de l'argent à gagner en restreignant la manière dont une bibliothèque pouvait être utilisée. J'ai probablement passé les 8 ou 9 premières années de ma carrière à suivre strictement cette "meilleure pratique".

Aujourd'hui, je crois que c'est un mauvais conseil. Parfois, il peut y avoir un argument raisonnable pour marquer une méthode privée, ou une classe finale, mais c'est extrêmement rare, et même alors, cela ne va probablement pas améliorer grand-chose.

Avez-vous déjà :

  • Été déçu, surpris ou blessé par une bibliothèque, etc., qui avait un bug qui aurait pu être corrigé avec l'héritage et quelques lignes de code, mais à cause de méthodes et classes privées/finales, vous avez dû attendre un correctif officiel qui pourrait ne jamais venir ? Moi si.
  • Eu envie d'utiliser une bibliothèque pour un cas d'utilisation légèrement différent de celui imaginé par les auteurs mais n'avez pas pu le faire à cause des méthodes et classes privées/finales ? Moi si.
  • Été déçu, surpris ou blessé par une bibliothèque, etc., qui était trop permissive dans son extensibilité ? Moi non.

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

Rationalisation #1 : C'est risqué et il n'y a aucune raison de remplacer une méthode spécifique

Je ne compte plus le nombre de fois où je me suis trompé sur la nécessité de remplacer une méthode spécifique que j'ai écrite. Ayant travaillé sur plusieurs libs open source populaires, j'ai appris à mes dépens le coût réel de marquer les choses comme privées. Cela élimine souvent la seule solution pratique aux problèmes ou cas d'utilisation imprévus. Au contraire, je n'ai jamais en 16+ années de développement professionnel regretté d'avoir marqué une méthode comme protégée plutôt que privée pour des raisons de sécurité de l'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. Point. Si c'est dangereux, notez-le dans la Javadoc de la classe/méthode, ne fermez pas simplement aveuglément la porte.

Marquer les méthodes comme protégées par défaut est une atténuation pour l'un des principaux problèmes du développement logiciel moderne : l'échec de l'imagination.

Rationalisation #2 : Cela garde l'API publique / la Javadoc propre

Celle-ci est plus raisonnable, et en fonction du public cible, cela pourrait même être la bonne chose à faire, mais il faut considérer quel est le coût de maintenir l'API "propre" : l'extensibilité. Pour les raisons évoquées ci-dessus, il est probablement plus judicieux de marquer les choses comme protégées par défaut, au cas où.

Rationalisation #3 : Mon logiciel est commercial et j'ai besoin de restreindre son utilisation.

Cela est également raisonnable, mais en tant que consommateur, je choisirais toujours le concurrent moins restrictif (en supposant qu'il n'y ait pas de différences significatives de qualité).

Jamais dire jamais

Je ne dis pas de ne jamais marquer les méthodes comme privées. Je dis que la meilleure règle à suivre est de "rendre les méthodes protégées à moins d'avoir une bonne raison de ne pas le faire".

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

0 votes

En ce qui concerne votre Rationalisation n° 1 - Quand je marque quelque chose comme protégé, je choisis de le faire explicitement car je pense que ce comportement n'est pas final et pourrait être un cas où mes classes enfants voudraient le remplacer. Si je ne suis pas sûr, je préférerais le rendre privé par défaut. Surtout dans un langage comme le C# qui garde une méthode non virtuelle par défaut, ajouter simplement un spécificateur d'accès protected n'a aucun sens. Vous devez ajouter à la fois les mots-clés virtual et protected pour rendre votre intention très claire. De plus, les gens préfèrent l'association à l'héritage, donc le protected par défaut est difficile à percevoir.

4 votes

La combinaison de mots-clés nécessaire pour rendre une méthode surchargeable est un détail de langage à mon avis. L'argument que je présente pour la rationalisation n°1 vise directement 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 pourrait être dangereux, alors il y a plus à gagner en optant pour l'extensibilité. Lorsque vous dites 'association', je prends cela comme un synonyme de composition, auquel cas je ne vois pas non plus l'importance.

3 votes

Je ne me souviens plus combien de fois j'ai dû "cloner" les classes sous-jacentes juste parce que je voulais remplacer 1 ou 2 des méthodes. Et comme tu l'as dit, avec la tendance sociale qui veut que nous partagions plus de code que jamais, il est beaucoup plus logique d'opter pour protected plutôt que private par défaut. Autrement dit, être plus ouvert à vos propres logiques.

28voto

Larry Points 444

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

Les commentaires ici semblent être largement favorables à l'utilisation des 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 que une règle d'or est de tout rendre privé quand vous n'êtes pas sûr est définitivement faux ! Vous ne verrez pas le problème tant que vous ne vous y heurterez pas. À mon avis, vous devriez marquer les champs comme protégés si vous n'êtes pas sûr.

Il y a deux cas où vous voulez étendre une classe :

  • Vous voulez ajouter des fonctionnalités supplémentaires à une classe de base
  • Vous voulez modifier une classe existante qui est 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 si frustrant le moment où vous découvrez que vous ne pouvez pas modifier les choses.

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

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

L'auteur de la bibliothèque pensait : il n'y a aucune raison pour que les utilisateurs de ma bibliothèque aient besoin d'accéder aux détails d'implémentation de assembleCar() n'est-ce pas ? Marquons donc screw comme privé.

Eh bien, l'auteur se trompe. Si vous souhaitez modifier uniquement la méthode assembleCar() sans copier toute la classe dans votre package, vous êtes malchanceux. Vous devez réécrire votre propre champ screw. 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 privées. À ce stade, ça commence à être pénible.

Oui, vous pouvez me contredire en disant que eh bien 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 soutiens pas que le champ privé est un problème avec 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 champ particulier. Si vous n'êtes pas sûr, marquez-le protégé pour que tout le monde soit plus heureux plus tard. Au moins, n'abusez pas des champs privés.

Je soutiens totalement la réponse de Nick.

3 votes

En utilisant des champs privés, on dit principalement que l'héritage est la mauvaise abstraction pour modifier un certain comportement d'une classe/objet (si utilisé consciemment). Altérer violera généralement silencieusement le LSP car le comportement est modifié sans que l'API change. Excellente réponse, +1.

0 votes

Prêcher! Private 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 corriger cela.

0 votes

Comme j'ai compris la réponse de Nick, il faisait référence principalement aux méthodes et non aux propriétés. Je suis totalement d'accord que nous ne devrions pas déclarer les méthodes privées à chaque fois et leur utilisation doit être réfléchie, mais pourquoi conseiller de déclarer les propriétés protégées plutôt que privées juste parce que vous voudriez "faciliter" la réécriture de la classe. Je partage totalement votre frustration car je travaille quotidiennement avec des tierces parties, mais encourageons les gens à créer une architecture solide et non pas juste dire "le code finirait par être nul alors mettons-le protégé dès le début pour faciliter la réécriture". Bien que je comprenne votre frustration.

16voto

Anurag Points 66470

J'ai lu un article il y a quelque temps qui parlait de verrouiller autant que possible chaque classe. Rendre tout final et privé sauf si vous avez un besoin immédiat d'exposer des données ou des fonctionnalités au monde extérieur. Il est toujours facile d'étendre la portée pour être plus permissif plus tard, mais pas l'inverse. Envisagez d'abord de rendre autant de choses que possible final, ce qui facilitera grandement le choix entre private et protected.

  1. Rendre toutes les classes finales sauf si vous avez besoin de les sous-classer tout de suite.
  2. Rendre toutes les méthodes finales sauf si vous avez besoin de les sous-classer et de les remplacer immédiatement.
  3. Rendre tous les paramètres de méthode finaux sauf si vous avez besoin de les modifier à l'intérieur de la méthode, ce qui est assez maladroit la plupart du temps de toute façon.

Maintenant, si vous vous retrouvez avec une classe finale, faites tout en privé sauf si quelque chose est absolument nécessaire pour le monde - rendez-le public.

Si vous vous retrouvez avec une classe qui a des sous-classes, examinez attentivement chaque propriété et méthode. Envisagez d'abord si vous voulez même exposer cette propriété/méthode aux sous-classes. Si c'est le cas, réfléchissez si une sous-classe peut causer des dégâts à votre objet si elle modifie la valeur de la propriété ou l'implémentation de la méthode en la surchargeant. 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 faites-la privée. Sinon, faites-la protégée.

Avis de non-responsabilité : Je ne programme pas beaucoup en Java :)

2 votes

Pour clarifier: Une classe final en Java est une classe qui ne peut pas être héritée. Une méthode final est non virtuelle, c'est-à-dire qu'elle ne peut pas être redéfinie par une sous-classe (les méthodes Java sont virtuelles par défaut). Un champ/paramètre/variable final est une référence variable immuable (dans le sens où elle ne peut pas être modifiée après son initialisation).

1 votes

Amusant, j'ai vu exactement le conseil inverse, que rien ne devrait être final sauf s'il est certain que la substitution de la chose en question a le potentiel de briser un invariant. De cette façon, tout le monde est libre d'étendre vos classes selon les besoins.

0 votes

Pouvez-vous citer un cas dans votre carrière de programmation où marquer un paramètre de méthode "final" a fait une différence dans votre compréhension? (autre que si requis par le compilateur)

7voto

Alok Save Points 115848

Quand utiliseriez-vous private et quand utiliseriez-vous protected ?

L'héritage privé peut être considéré comme une relation Implémentée en termes de plutôt qu'une relation EST UN. En d'autres termes, l'interface externe de la classe héritante n'a aucun (visible) lien avec la classe héritée. Elle utilise l'héritage private uniquement pour implémenter une fonctionnalité similaire à celle fournie par la classe de 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 EST UN type de la classe de base et souhaite restreindre l'accès des membres dérivés uniquement à la classe dérivée.

2 votes

Le concept de "Implementé 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 des 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 toute héritage de celles-ci.

2 votes

Intéressant processus de réflexion - Héritage privé. @shawnhcorey merci d'avoir rendu explicite le fait qu'il pointe vers une relation A-A aussi connue sous le nom d'association (qui peut être une agrégation ou une composition). Je pense que cet article devrait également être mis à jour pour clarifier cela.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X