Question

J'utilise rarement l'héritage, mais quand je le fais, je n'utilise jamais d'attributs protégés car je pense que cela casse l'encapsulation des classes héritées.

Utilisez-vous des attributs protégés? à quoi les utilisez-vous?

Était-ce utile?

La solution

Dans cette interview sur Design de Bill Venners, Joshua Bloch, l'auteur de < em> Effective Java dit:

  

Confiance des sous-classes

     

Bill Venners: Dois-je faire confiance aux sous-classes plus intimement que   non-sous-classes? Par exemple, est-ce que je fais   c'est plus facile pour une sous-classe   la mise en œuvre de me casser que je   serait pour un non-sous-classe? Dans   en particulier, comment vous sentez-vous   données protégées?

     

Josh Bloch: pour écrire quelque chose qui est à la fois sous-classable et robuste   contre une sous-classe malveillante est   en fait une chose assez difficile à faire,   en supposant que vous donniez l'accès à la sous-classe   à vos structures de données internes. Si   la sous-classe n'a pas accès à   tout ce qu'un utilisateur ordinaire   pas, alors il est plus difficile pour le   sous-classe à faire des dégâts. Mais à moins que vous   rendez toutes vos méthodes finales, la   sous-classe peut encore casser votre   contrats en faisant juste le mauvais   les choses en réponse à la méthode   invocation. C'est précisément pourquoi le   classes critiques de sécurité comme String   sont finales. Sinon quelqu'un pourrait   écrire une sous-classe qui fabrique des chaînes   semble mutable, ce qui serait   suffisant pour casser la sécurité. Donc vous   doit faire confiance à vos sous-classes. Si vous   ne leur faites pas confiance, alors vous ne pouvez pas permettre   eux, car les sous-classes peuvent si facilement   amener une classe à violer sa   contrats.

     

En ce qui concerne les données protégées en général,   c'est un mal nécessaire. CA devrait etre   gardé au minimum. La plupart des données protégées   et méthodes protégées équivalent à   s'engager dans une implémentation   détail. Un champ protégé est un   détail de mise en œuvre que vous êtes   rendre visible aux sous-classes. Même un   méthode protégée est un morceau de   structure interne que vous faites   visible par les sous-classes.

     

La raison pour laquelle vous le rendez visible est que   il est souvent nécessaire pour permettre   sous-classes à faire leur travail, ou à faire   c'est efficace. Mais une fois que vous avez terminé   vous y êtes engagé. C'est maintenant   quelque chose que vous n'êtes pas autorisé à   changer, même si vous trouvez plus tard un   mise en œuvre efficace que non   plus implique l'utilisation d'un   domaine ou méthode particulière.

     

Donc, toutes choses égales par ailleurs, vous   ne devrait pas avoir de membres protégés   du tout. Mais cela dit, si vous en avez aussi   peu, votre classe peut ne pas être utilisable   comme une super classe, ou du moins pas comme   une super classe efficace. Souvent vous   découvrez après le fait. Ma philosophie   est d'avoir aussi peu de membres protégés que   possible lorsque vous écrivez le   classe. Ensuite, essayez de le sous-classer. Vous   peut découvrir que sans un particulier   méthode protégée, toutes les sous-classes   faire quelque chose de mal.

     

Par exemple, si vous regardez    AbstractList , vous constaterez qu'il existe   est une méthode protégée pour supprimer un   gamme de la liste en un seul coup   ( removeRange ). Pourquoi est-ce là-dedans?   Parce que le langage normal pour enlever un   gamme, basée sur l'API publique, est de   appelez subList pour obtenir une sous- liste ,   puis appelez clear à ce sujet   sous- liste . Sans ce particulier   méthode protégée, cependant, le seul   Ce que clear pourrait faire est   supprimer à plusieurs reprises des éléments individuels.

     

Pensez-y. Si vous avez un tableau   représentation, que va-t-il faire? Il   va à plusieurs reprises réduire le tableau,   faire l'ordre n travail N fois. Alors ça va   prendre une quantité de travail quadratique,   au lieu de la quantité linéaire de travail   qu'il le devrait. En fournissant cette   méthode protégée, nous permettons à   mise en œuvre qui peut efficacement   supprimer une plage entière pour le faire. Et   toute implémentation List raisonnable   peut supprimer une plage plus efficacement   tout à la fois.

     

Que nous en aurions besoin   la méthode est quelque chose que vous auriez à   être bien plus intelligent que moi pour savoir   de face. Fondamentalement, j'ai implémenté le   chose. Puis, comme nous avons commencé à sous-classe   nous avons réalisé que la plage était effacée   quadratique. Nous ne pouvions pas nous permettre cela, alors   Je mets dans la méthode protégée. je pense   c'est la meilleure approche avec   méthodes protégées. Mettre dans aussi peu que   possible, puis ajoutez-en au besoin.   Les méthodes protégées représentent   engagements à des conceptions que vous pouvez   vouloir changer. Vous pouvez toujours ajouter   méthodes protégées, mais vous ne pouvez pas prendre   les sortir.

     

Bill Venners: Et des données protégées?

     

Josh Bloch: La même chose, mais plus encore. Les données protégées, c'est encore plus   dangereux en termes de gâcher votre   invariants de données. Si vous donnez à quelqu'un   sinon l'accès à certaines données internes,   ils en ont le règne libre.

Version courte: il casse l’encapsulation, mais c’est un mal nécessaire qui doit être limité au minimum.

Autres conseils

C #:

J'utilise protected pour les méthodes abstraites ou virtuelles que je veux que les classes de base remplacent. Je crée également une méthode protégée si elle peut être appelée par des classes de base, mais je ne veux pas qu'elle soit appelée en dehors de la hiérarchie des classes.

Vous en aurez peut-être besoin pour l'attribut statique (ou "global") dont vous souhaitez que vos sous-classes ou classes du même package (s'il s'agit de Java).

Ces attributs finaux statiques représentant une sorte de "valeur constante" ont rarement une fonction de lecture, un attribut final statique protégé peut donc être utile dans ce cas.

Scott Meyers indique que ne n'utilisez pas d'attributs protégés dans Effective C ++ (3 e éd.):

  

Point 22: Déclarez les membres de données privés.

La raison est la même que vous donnez: cela casse les encapsulations. La conséquence est que sinon, des modifications locales dans la présentation de la classe pourraient rompre les types dépendants et entraîner des modifications à de nombreux autres endroits.

Il n'y a jamais de bonnes raisons d'avoir des attributs protégés. Une classe de base doit pouvoir dépendre de l'état, ce qui implique de restreindre l'accès aux données par le biais de méthodes d'accès. Vous ne pouvez donner à personne l'accès à vos données personnelles, même à vos enfants.

Le mot clé protected est une erreur conceptuelle et une mauvaise conception des langues, ainsi que plusieurs langues modernes, telles que Nim et Ceylan (voir http://ceylon-lang.org/documentation/faq/language-design/#no_protected_modifier ), qui a été soigneusement conçu plutôt que copiez simplement les erreurs courantes, ne possédez pas un tel mot clé.

Ce ne sont pas les membres protégés qui rompent l’encapsulation, ils exposent les membres qui ne devraient pas être exposés mais qui cassent l’encapsulation ... peu importe qu’ils soient protégés ou publics. Le problème avec protected est qu’il est erroné et trompeur ... déclarer des membres protected (plutôt que privé ) ne les protège pas, il fait le contraire, exactement comme public . Un membre protégé, accessible en dehors de la classe, est exposé au monde et sa sémantique doit donc être conservée pour toujours, comme dans le cas de public . L’idée même de " protégé " est absurde ... l'encapsulation n'est pas une sécurité, et le mot clé ne fait que favoriser la confusion entre les deux. Vous pouvez aider un peu en évitant toute utilisation de protected dans vos propres classes - si quelque chose fait partie interne de l'implémentation, ne fait pas partie de la sémantique de la classe et peut changer dans le futur, puis le rendre privé ou interne à votre paquet, module, assemblage, etc. S'il s'agit d'une partie non modifiable de la sémantique de la classe, alors rendez-le public, et vous n'ennuierez pas les utilisateurs de votre classe qui peuvent voir qu'il existe une utilité utile. membre dans la documentation, mais ne peut pas l'utiliser, à moins qu'ils ne créent leurs propres instances et qu'ils puissent y accéder en sous-classant.

Je n'utilise pas d'attributs protégés en Java, car ils ne sont protégés que par paquets. Mais en C ++, je les utiliserai dans des classes abstraites, ce qui permettra à la classe héritière de les hériter directement.

En général, non, vous ne voulez vraiment pas utiliser les membres de données protégés. Ceci est d'autant plus vrai si vous écrivez une API. Une fois que quelqu'un hérite de votre classe, vous ne pouvez jamais vraiment faire de maintenance et ne pas le casser de façon bizarre et parfois sauvage.

Je pense que les attributs protégés sont une mauvaise idée. J'utilise CheckStyle pour appliquer cette règle à mes équipes de développement Java.

J'ai récemment travaillé sur un projet qui était "protégé". Le député était une très bonne idée. La hiérarchie de classe était quelque chose comme:

[+] Base
 |
 +--[+] BaseMap
 |   |
 |   +--[+] Map
 |   |
 |   +--[+] HashMap
 |
 +--[+] // something else ?

La base a implémenté std :: list mais rien d’autre. L’accès direct à la liste était interdit à l’utilisateur, mais la classe de base étant incomplète, elle s’appuyait néanmoins sur les classes dérivées pour implémenter l’indirection vers la liste.

L'indirection peut provenir d'au moins deux types: std :: map et stdext :: hash_map. Les deux cartes se comporteront de la même manière, mais hash_map a besoin que la clé soit hashable (dans VC2003, convertible en taille_t).

BaseMap a donc implémenté un TMap en tant que type basé sur un modèle qui était un conteneur semblable à une carte.

Map et HashMap sont deux classes dérivées de BaseMap, l'une spécialisée BaseMap sur std :: map et l'autre sur stdext :: hash_map.

Donc:

  • La base n'était pas utilisable en tant que telle (aucun accesseur public!) et ne fournissait que des fonctionnalités et du code communs

  • BaseMap avait besoin de lire / écrire facilement dans un std :: list

  • Map et HashMap avaient besoin d’un accès facile en lecture / écriture au TMap défini dans BaseMap.

Pour moi, la seule solution consistait à utiliser protected pour les variables de membre std :: list et TMap. Il était hors de question que je mette ceux-ci "privés". car je voudrais de toute façon exposer toutes ou presque toutes leurs fonctionnalités via des accesseurs lecture / écriture.

En fin de compte, je suppose que si vous divisez votre classe en plusieurs objets, chaque dérivation ajoutant les fonctionnalités nécessaires à sa classe mère, et seule la classe la plus dérivée pouvant être réellement utilisée est protégée. Le fait que le " membre protégé " était une classe, et donc, il était presque impossible de "casser", aidé.

Mais sinon, vous devez éviter autant que possible les objets protégés (c'est-à-dire, utilisez private par défaut et public lorsque vous devez exposer la méthode).

Je les utilise. En bref, c’est un bon moyen de partager certains attributs. Certes, vous pouvez écrire des fonctions set / get pour eux, mais s’il n’ya pas de validation, quel est le but? C'est aussi plus rapide.

Considérez ceci: vous avez une classe qui est votre classe de base. Il a pas mal d'attributs que vous ne pouvez pas utiliser dans les objets enfants. Vous pouvez écrire une fonction get / set pour chacun ou simplement les définir.

Mon exemple typique est un gestionnaire de fichiers / flux. Vous souhaitez accéder au gestionnaire (descripteur de fichier), mais vous souhaitez le masquer aux autres classes. C'est beaucoup plus simple que d'écrire une fonction set / get pour elle.

En général, oui. Une méthode protégée est généralement préférable.

En utilisation, l'utilisation d'une variable finale protégée pour un objet partagé par tous les enfants d'une classe offre un niveau de simplicité donné. Je conseillerais toujours de ne pas l'utiliser avec des primitives ou des collections, car les contrats sont impossibles à définir pour ces types.

Dernièrement, je suis venu séparer ce que vous faites des primitives et des collections brutes de celui que vous faites des classes bien formées. Les primitives et les collections doivent TOUJOURS être privées.

De plus, j'ai commencé à exposer de temps en temps les variables de membre public lorsqu'elles sont déclairées comme étant définitives et constituent des classes bien formées qui ne sont pas trop flexibles (encore une fois, pas de primitives ou de collections).

Ce n’est pas un raccourci stupide, j’y ai pensé sérieusement et j’ai décidé qu’il n’y avait absolument aucune différence entre une variable publique finale exposant un objet et un getter.

Cela dépend de ce que vous voulez. Si vous voulez une classe rapide, les données doivent être protégées et utiliser des méthodes protégées et publiques. Parce que je pense que vous devriez supposer que vos utilisateurs issus de votre classe connaissent très bien votre classe ou au moins ils ont lu votre manuel au moment de leur utilisation.

Si vos utilisateurs dérangent votre classe, ce n’est pas votre problème. Tout utilisateur malveillant peut ajouter les lignes suivantes lors du remplacement d’un de vos virtuels:

(C #)

static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...

Vous ne pouvez pas sceller toutes les classes pour empêcher cela.

Bien sûr, si vous voulez une contrainte sur certains de vos champs, utilisez des propriétés ou des propriétés d'accesseur ou quelque chose que vous préférez, et rendez ce champ privé car il n'y a pas d'autre solution ...

Mais personnellement, je n'aime pas m'appuyer à tout prix sur les principes oop. Créer des propriétés dans le seul but de rendre privées les membres de données.

(C #):

private _foo;
public foo
{
   get {return _foo;}
   set {_foo=value;}
}

C’était mon opinion personnelle.

Mais faites ce que votre patron exige (s'il veut des champs privés, faites-le.)

J'utilise des variables / attributs protégés dans des classes de base que je ne prévois pas de transformer en méthodes. De cette manière, les sous-classes ont un accès complet à leurs variables héritées et n'ont pas la charge (créée artificiellement) de passer par des getters / setters pour y accéder. Un exemple est une classe utilisant un flux d'E / S sous-jacent; il y a peu de raisons de ne pas autoriser l'accès direct des sous-classes au flux sous-jacent.

Cela convient très bien aux variables membres qui sont utilisées de manière simple et directe au sein de la classe de base et de toutes les sous-classes. Mais pour une variable dont l'utilisation est plus compliquée (par exemple, son accès provoque des effets secondaires chez d'autres membres de la classe), une variable directement accessible n'est pas appropriée. Dans ce cas, il peut être rendu privé et des getters / setters publics / protégés peuvent être fournis à la place. Un exemple est un mécanisme de mise en mémoire tampon interne fourni par la classe de base, dans lequel accéder directement aux tampons à partir d’une sous-classe compromettrait l’intégrité des algorithmes utilisés par la classe de base pour les gérer.

Il s’agit d’un jugement de conception, basé sur la simplicité de la variable membre et sur la manière dont elle devrait l'être dans les versions futures.

L’encapsulation est excellente, mais elle peut être poussée trop loin. J'ai vu des classes dont les méthodes privées ont accédé à ses variables de membre en utilisant uniquement les méthodes getter / setter. C'est exagéré, car si une classe ne peut pas faire confiance à ses propres méthodes privées avec ses propres données privées, à qui peut-elle faire confiance?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top