Devriez-vous utiliser des variables membres protégées?

97

Devriez-vous utiliser des variables membres protégées? Quels sont les avantages et quels problèmes cela peut-il causer?

John Channing
la source

Réponses:

73

Devriez-vous utiliser des variables membres protégées?

Cela dépend de la difficulté avec laquelle vous vous cachez.

  • Si vous ne voulez pas de fuite d'état interne, alors déclarer toutes vos variables membres comme privées est la voie à suivre.
  • Si vous ne vous souciez pas vraiment du fait que les sous-classes peuvent accéder à l'état interne, alors protected est assez bonne.

Si un développeur arrive et sous-classe votre classe, il risque de le gâcher parce qu'il ne le comprend pas complètement. Avec les membres privés, autres que l'interface publique, ils ne peuvent pas voir les détails spécifiques de l'implémentation de la façon dont les choses sont faites, ce qui vous donne la flexibilité de le changer plus tard.

Allain Lalonde
la source
1
Pouvez-vous commenter les performances des variables protégées par rapport à une variable privée avec une méthode get / set?
Jake
3
Je dirais que ce n'est pas quelque chose qui vaut la peine de s'inquiéter à moins que vous ne trouviez par le profilage que le goulot de la bouteille finit par être les accesseurs (ce qui n'est presque jamais). Il existe des astuces qui peuvent être faites pour rendre le JIT plus intelligent sur les choses si cela finit par être un problème. En java par exemple, vous pouvez indiquer que l'accesseur peut être inséré en le marquant comme final. Bien qu'honnêtement, les performances des getters et des setters sont beaucoup moins importantes que de gérer l'organisation du système et les problèmes de performances réels déterminés par un profileur.
Allain Lalonde
23
@Jake: Vous ne devez jamais prendre de décisions de conception basées sur des hypothèses de performances. Vous prenez des décisions de conception en fonction de ce que vous pensez être la meilleure conception et ce n'est que si votre profilage réel montre un goulot d'étranglement dans votre conception, vous allez le réparer. Habituellement, si le design est solide, les performances sont également bonnes.
Mecki
Avec les membres privés, à part l'interface publique, ils ne peuvent pas voir les détails spécifiques de l'implémentation.Ils peuvent simplement ouvrir la classe et la rechercher, donc cela n'a aucun sens?!
Black
2
@Black Clairement Allain signifiait «ils ne peuvent pas accéder » à ces membres et ne peuvent donc pas construire de code contre eux, laissant l'auteur de la classe libre de supprimer / modifier les membres protégés plus tard. (Bien sûr, l'idiome pimpl permettrait de les masquer visuellement et des unités de traduction, y compris l'en-tête, aussi.)
underscore_d
31

Le sentiment général de nos jours est qu'ils provoquent un couplage indu entre les classes dérivées et leurs bases.

Ils n'ont aucun avantage particulier par rapport aux méthodes / propriétés protégées (il était une fois, ils pouvaient avoir un léger avantage en termes de performances), et ils étaient également davantage utilisés à une époque où l'héritage très profond était à la mode, ce qui n'est pas le cas pour le moment.

Will Dean
la source
2
Ne devrait pas l' no particular advantage over protected methods/propertiesêtre no particular advantage over *private* methods/properties?
Penghe Geng
Non, parce que je parle / parlais des avantages / inconvénients des différentes manières de communiquer entre les classes dérivées et leurs bases - toutes ces techniques seraient `` protégées '' - la différence est de savoir s'il s'agit de variables membres (champs) ou de propriétés / méthodes ( c'est-à-dire des sous-programmes d'une certaine sorte).
Will Dean
1
Merci pour la clarification rapide. Je suis heureux d'avoir la réponse originale de l'affiche dans une heure à ma question à un poste de 6 ans. Vous ne pensez pas que cela peut arriver dans la plupart des autres forums en ligne :)
Penghe Geng
9
Plus remarquable encore, c'est que je suis en fait d'accord avec moi-même sur ce laps de temps ...
Will Dean
L'un des objectifs d'un constructeur est de veiller à ce que toutes les variables d'état soient explicitement initialisées. Si vous adhérez à cette convention, vous pouvez utiliser la superconstruction pour appeler le constructeur parent; il s'occupera ensuite d'initialiser les variables d'état privées dans la classe parente.
ncmathsadist
31

Généralement, si quelque chose n'est pas délibérément conçu comme public, je le rends privé.

Si une situation survient où j'ai besoin d'accéder à cette variable ou méthode privée à partir d'une classe dérivée, je la change de privé en protégé.

Cela n'arrive presque jamais - je ne suis vraiment pas du tout fan de l'héritage, car ce n'est pas un moyen particulièrement efficace de modéliser la plupart des situations. En tout cas, continuez, pas de soucis.

Je dirais que c'est bien (et probablement la meilleure façon de procéder) pour la majorité des développeurs.

Le simple fait de la question est si un autre développeur arrive un an plus tard et décide qu'il a besoin d'accéder à votre variable de membre privé, il va simplement modifier le code, le changer en protégé et poursuivre ses activités.

Les seules vraies exceptions à cela sont si vous êtes dans le domaine de l'expédition de DLL binaires sous forme de boîte noire à des tiers. Il s'agit essentiellement de Microsoft, de ces fournisseurs de «Custom DataGrid Control» et peut-être de quelques autres grandes applications livrées avec des bibliothèques d'extensibilité. À moins que vous ne soyez dans cette catégorie, cela ne vaut pas la peine de consacrer du temps / des efforts à vous soucier de ce genre de choses.

Orion Edwards
la source
8

Le problème clé pour moi est qu'une fois que vous faites une variable protégée, vous ne pouvez pas autoriser aucune méthode de votre classe à compter sur sa valeur dans une plage, car une sous-classe peut toujours la placer hors de plage.

Par exemple, si j'ai une classe qui définit la largeur et la hauteur d'un objet pouvant être rendu, et que je protège ces variables, je ne peux alors faire aucune hypothèse sur (par exemple) le rapport hauteur / largeur.

De manière critique, je ne peux jamais faire ces hypothèses à aucun moment à partir du moment où le code est publié en tant que bibliothèque, car même si je mets à jour mes setters pour maintenir le rapport hauteur / largeur, je n'ai aucune garantie que les variables sont définies via les setters ou accessibles via le getters dans le code existant.

Aucune sous-classe de ma classe ne peut non plus choisir de faire cette garantie, car elle ne peut pas non plus appliquer les valeurs des variables, même si c'est tout l'intérêt de sa sous-classe .

Par exemple:

  • J'ai une classe de rectangle dont la largeur et la hauteur sont stockées en tant que variables protégées.
  • Une sous-classe évidente (dans mon contexte) est une classe "DisplayedRectangle", où la seule différence est que je limite les largeurs et hauteurs à des valeurs valides pour un affichage graphique.
  • Mais c'est impossible maintenant , car ma classe DisplayedRectangle ne peut pas vraiment contraindre ces valeurs, car toute sous-classe de celle-ci pourrait remplacer les valeurs directement, tout en étant toujours traitée comme un DisplayedRectangle.

En contraignant les variables à être privées, je peux alors appliquer le comportement que je souhaite via des setters ou des getters.

dépouiller
la source
7

En général, je conserverais vos variables membres protégées dans les rares cas où vous avez un contrôle total sur le code qui les utilise également. Si vous créez une API publique, je dirais jamais. Ci-dessous, nous ferons référence à la variable membre comme une «propriété» de l'objet.

Voici ce que votre superclasse ne peut pas faire après avoir rendu une variable membre protégée plutôt que privée avec des accesseurs:

  1. créer paresseusement une valeur à la volée lorsque la propriété est en cours de lecture. Si vous ajoutez une méthode getter protégée, vous pouvez créer paresseusement la valeur et la renvoyer.

  2. savoir quand la propriété a été modifiée ou supprimée. Cela peut introduire des bogues lorsque la superclasse émet des hypothèses sur l'état de cette variable. Faire une méthode de définition protégée pour la variable conserve ce contrôle.

  3. Définissez un point d'arrêt ou ajoutez une sortie de débogage lorsque la variable est lue ou écrite.

  4. Renommez cette variable membre sans chercher dans tout le code qui pourrait l'utiliser.

En général, je pense que ce serait le cas rare où je recommanderais de créer une variable membre protégée. Vous feriez mieux de passer quelques minutes à exposer la propriété via des getters / setters que des heures plus tard pour rechercher un bogue dans un autre code qui a modifié la variable protégée. Non seulement cela, mais vous êtes assuré contre l'ajout de fonctionnalités futures (telles que le chargement paresseux) sans casser le code dépendant. Il est plus difficile de le faire plus tard que de le faire maintenant.

Michael Bishop
la source
7

Au niveau de la conception, il peut être approprié d'utiliser une propriété protégée, mais pour l'implémentation, je ne vois aucun avantage à la mapper sur une variable membre protégée plutôt que sur des méthodes accesseur / mutateur.

Les variables membres protégées présentent des inconvénients importants car elles permettent au code client (la sous-classe) d'accéder à l'état interne de la classe de base. Cela empêche la classe de base de maintenir efficacement ses invariants.

Pour la même raison, les variables membres protégées rendent également l'écriture de code multithread sécurisé beaucoup plus difficile à moins d'être garantie constante ou confinée à un seul thread.

Les méthodes accesseur / mutateur offrent une stabilité d'API et une flexibilité de mise en œuvre considérablement plus élevées en maintenance.

De plus, si vous êtes un puriste OO, les objets collaborent / communiquent en envoyant des messages, pas en lisant / en définissant l'état.

En retour, ils offrent très peu d'avantages. Je ne les supprimerais pas nécessairement du code de quelqu'un d'autre, mais je ne les utilise pas moi-même.

richj
la source
4

La plupart du temps, il est dangereux d'utiliser protected car vous cassez quelque peu l'encapsulation de votre classe, qui pourrait bien être décomposée par une classe dérivée mal conçue.

Mais j'ai un bon exemple: disons que vous pouvez une sorte de conteneur générique. Il a une implémentation interne et des accesseurs internes. Mais vous devez offrir au moins 3 accès publics à ses données: map, hash_map, vector-like. Ensuite, vous avez quelque chose comme:

template <typename T, typename TContainer>
class Base
{
   // etc.
   protected
   TContainer container ;
}

template <typename Key, typename T>
class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }

template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }

template <typename T>
class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }

J'ai utilisé ce genre de code il y a moins d'un mois (donc le code est de mémoire). Après réflexion, je pense que même si le conteneur Base générique devrait être une classe abstraite, même s'il peut très bien vivre, car utiliser directement Base serait tellement pénible qu'il devrait être interdit.

Résumé Ainsi, vous avez protégé les données utilisées par la classe dérivée. Néanmoins, nous devons tenir compte du fait que la classe Base doit être abstraite.

Paercebal
la source
il ne rompt pas l'encapsulation plus que les membres du public. C'est un paramètre pour dire que les classes dérivées peuvent utiliser l'état de la classe qui n'est pas exposé aux utilisateurs de la classe.
gbjbaanb
@gbjbaanb: Vous vous contredisez "cela ne rompt pas l'encapsulation plus que les membres publics" est différent de "[seules] les classes dérivées peuvent utiliser l'état de la classe". «protégé» est le milieu entre le public et le privé. Donc "protégé [...] casse quelque peu l'encapsulation" est toujours vrai ...
paercebal
en fait, dans le langage C ++, les adaptateurs de conteneur comme std :: stack exposeront l'objet conteneur sous-jacent avec une variable protégée appelée "c".
Johannes Schaub - litb
Je sais que ce post est assez ancien, mais je ressens le besoin d'intervenir. Vous ne rompez pas "quelque peu" l'encapsulation, vous la cassez complètement. protectedn'est pas plus encapsulé que public. Je suis prêt à avoir tort. Tout ce que vous avez à faire est d'écrire une classe avec un membre protégé et de m'interdire de le modifier. De toute évidence, la classe doit être non finale, car tout l'intérêt d'utiliser protected est l'héritage. Soit quelque chose est encapsulé, soit ce n'est pas le cas. Il n'y a pas d'état intermédiaire.
Taekahn
3

Bref oui.

Les variables membres protégées permettent d'accéder à la variable à partir de toutes les sous-classes ainsi que de toutes les classes du même package. Cela peut être très utile, en particulier pour les données en lecture seule. Cependant, je ne pense pas qu'ils soient jamais nécessaires, car toute utilisation d'une variable membre protégée peut être répliquée à l'aide d'une variable membre privé et de quelques getters et setters.

Jay Stramel
la source
1
Inversement, les variables de membre privé ne sont également jamais nécessaires; public est suffisant pour tout usage.
Alice
3

Pour mémoire, sous le point 24 de "C ++ exceptionnel", dans l'une des notes de bas de page, Sutter dit "vous n'écririez jamais une classe qui a une variable membre publique ou protégée. N'est-ce pas? (Indépendamment du mauvais exemple défini par certaines bibliothèques .) "

hAcKnRoCk
la source
2

Pour plus d'informations sur les modificateurs d'accès .Net, cliquez ici

Il n'y a pas de réels avantages ou inconvénients aux variables membres protégées, c'est une question de ce dont vous avez besoin dans votre situation spécifique. En général, il est courant de déclarer les variables membres comme privées et de permettre l'accès extérieur via les propriétés. De plus, certains outils (par exemple certains mappeurs O / R) s'attendent à ce que les données d'objet soient représentées par des propriétés et ne reconnaissent pas les variables membres publiques ou protégées. Mais si vous savez que vous voulez que vos sous-classes (et UNIQUEMENT vos sous-classes) accèdent à une certaine variable, il n'y a aucune raison de ne pas la déclarer comme protégée.

Manu
la source
Vouloir que des sous-classes accèdent à une variable est très différent de vouloir qu'elles puissent la muter librement . C'est l'un des principaux arguments contre les variables protégées: maintenant, votre classe de base ne peut pas supposer qu'aucun de ses invariants ne tient, car toute classe dérivée peut absolument tout faire avec les membres protégés. C'est le principal argument contre eux. S'ils ont juste besoin d' accéder aux données, alors ... écrivez un accesseur. : P (j'utilise des variables protégées, bien que probablement plus que je ne le devrais, et je vais essayer de réduire!)
underscore_d