web-dev-qa-db-fra.com

Privé vs protégé - Visibilité Préoccupation liée aux bonnes pratiques

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

  • public - Toute classe/fonction peut accéder à la méthode/propriété.
  • protected - Seules 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é. Il ne sera même pas hérité.

Tout va bien, la question est la suivante: quelle est la différence pratique entre eux? Quand utiliseriez-vous private et quand utiliseriez-vous protected? Existe-t-il une norme ou une bonne pratique acceptable concernant celle-ci?

Jusqu'à présent, pour conserver le concept d'héritage et de polymorphisme, j'utilisais public pour tout ce qui devait être accessible de l'extérieur (comme les constructeurs et la fonctionnalité de classe principale), et protected pour 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 une question comme celle-ci SO).

133
Madara Uchiha

Non, vous n'êtes pas sur la bonne voie. Une bonne règle est la suivante: faites en sorte que tout soit 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 à l'aide de votre classe.

Si vous concevez votre classe de manière à pouvoir être héritée, choisissez avec soin ce qui peut être remplacé et accessible depuis les sous-classes et rendez-la protégée (et finale, parlons de Java, si vous voulez la rendre accessible mais non remplaçable). Sachez toutefois 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 rupture des 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 (privé à protégé, final à non final) à des fins de test unitaire, puis documentez-le et indiquez clairement que, bien que la méthode soit protégée, elle n'est pas supposée être remplacée.

123
JB Nizet

Permettez-moi de commencer par dire que je parle principalement d’accès à une méthode ici, et dans une moindre mesure, de marquer les classes comme final, not accès membre.

La vieille sagesse

"Marquez-le privé à moins que vous n'ayez une bonne raison de ne pas"

avait du sens à l'époque de sa rédaction, avant que l'open source ne domine l'espace de la bibliothèque de développement et la gestion de VCS/dependency. est devenu hyper collaboratif grâce à Github, Maven, etc. À l'époque, il y avait aussi de l'argent à gagner en limitant 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 à adhérer strictement à cette "meilleure pratique".

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

As-tu déjà:

  • Avez-vous été déçu, surpris ou blessé par une bibliothèque, etc. avec 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é forcées d'attendre un correctif officiel qui pourrait ne jamais arriver? J'ai.
  • Vous souhaitez utiliser une bibliothèque pour un cas d'utilisation légèrement différent de celui imaginé par les auteurs, mais vous ne pouvez pas le faire en raison de méthodes et de classes privées/finales? J'ai.
  • Avez-vous été déçu, surpris ou blessé par une bibliothèque, etc. trop permissive quant à son extensibilité? Je n'ai pas.

Voici les trois principales rationalisations que j'ai entendues pour les méthodes de marquage privées par défaut:

Rationalisation n ° 1: c'est dangereux 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é de remplacer une méthode spécifique que j'ai écrite. Ayant travaillé sur plusieurs bibliothèques populaires open source, j'ai appris à la dure le coût réel du marquage des objets privés. Cela élimine souvent la seule solution pratique aux problèmes imprévus ou aux cas d'utilisation. À l'inverse, je n'ai jamais plus de 16 ans de formation professionnelle regretté de ne pas avoir sélectionné une méthode protégée plutôt que 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 fermez pas aveuglément la porte.

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

Rationalisation n ° 2: il 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 il convient de considérer le coût que représente le maintien d'une API "propre": l'extensibilité. Pour les raisons mentionnées ci-dessus, il est probablement plus logique de marquer les éléments protégés par défaut au cas où.

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

C'est également raisonnable, mais en tant que consommateur, je choisirais chaque fois un concurrent moins restrictif (en supposant qu'il n'y ait pas de différence de qualité significative).

Ne jamais dire jamais

Je ne dis pas que les méthodes ne sont jamais privées. Je dis que la meilleure règle à suivre est de "protéger les méthodes sauf s'il existe une bonne raison de ne pas le faire".

Ce conseil convient mieux à ceux qui travaillent sur des bibliothèques ou des projets de plus grande envergure divisés en modules. Pour les projets plus petits ou plus monolithiques, cela n'a pas tellement d'importance puisque vous contrôlez tout le code de toute façon et qu'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 :-)

46
Nick

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

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

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

Il y a deux cas où vous souhaitez prolonger une classe:

  • Vous souhaitez ajouter des fonctionnalités supplémentaires à une classe de base
  • Vous voulez modifier une classe existante qui est en dehors du paquet 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 des personnes abusent de champs privés le rend tellement frustrant de constater que vous ne pouvez pas modifier la merde.

Prenons 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’ya aucune raison que les utilisateurs de ma bibliothèque aient besoin d’accéder aux détails d’implémentation de assembleCar() non? Marquons vis comme privé.

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

Oui, vous pouvez argumenter avec moi que et que l'auteur de la bibliothèque aurait pu écrire un meilleur code, il n'y a donc aucun problème avec les champs privés . Je ne dis pas que privé champ est un problème avec [~ # ~] oop [~ # ~] . C'est un problème quand 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 protected afin que tout le monde soit heureux plus tard. Au moins, n'abusez pas du champ privé.

J'appuie beaucoup la réponse de Nick.

18
Larry

J'ai lu un article il y a quelque temps qui parlait de verrouiller chaque classe autant que possible. Rendez tout définitif et privé sauf si vous avez un immédiat besoin d'exposer des données ou des fonctionnalités au monde extérieur. Il est toujours facile d’élargir le champ d’application pour être plus admissible ultérieurement, mais pas l’inverse. Envisagez d’abord de faire autant de choses que possible final ce qui facilitera le choix entre private et protected.

  1. Faites en sorte que toutes les classes soient finales, sauf si vous devez immédiatement les sous-classer.
  2. Faites en sorte que toutes les méthodes soient finales à moins que vous n’ayez besoin de sous-classe et de les remplacer immédiatement.
  3. Définissez tous les paramètres de la méthode comme définitifs, sauf si vous devez 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, si vous vous retrouvez avec une dernière classe, alors rendez tout privé sauf si le monde a absolument besoin de quelque chose - rendez cela public.

Si vous vous retrouvez avec une classe qui possède des sous-classes, examinez soigneusement chaque propriété et méthode. Commencez par considérer 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 causer des ravages sur votre objet si elle a gâché la valeur de la propriété ou l'implémentation de la méthode en cours de remplacement. Si c'est possible et que vous voulez protéger la propriété/méthode de votre classe, même à partir de sous-classes (sons ironiques, je le sais), alors rendez-la privée. Sinon, protégez-le.

Disclaimer: Je ne programme pas beaucoup dans Java :)

16
Anurag

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

héritage privé peut être pensé de mis en œuvre en termes de relation plutôt que d'un IS-A relation. En termes simples, l'interface externe de la classe héritante n'a aucune relation (visible) avec la classe héritée. Elle utilise l'héritage private uniquement pour implémenter une fonctionnalité similaire à celle fournie par la classe Base.

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

7
Alok Save

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

1
ujwal dhakal