Quel est le problème précis de l'autorisation des getters?


15

Je ne cherche pas une opinion sur la sémantique, mais simplement un cas où avoir des getters judicieusement utilisés est un réel obstacle. Peut-être que cela me jette dans une spirale sans fin de compter sur eux, peut-être que l'alternative est plus propre et gère les getters automatiquement, etc. Quelque chose de concret.

J'ai entendu tous les arguments, j'ai entendu dire qu'ils sont mauvais parce qu'ils vous forcent à traiter des objets comme des sources de données, qu'ils violent «l'état pur» d'un objet de «ne donnez pas trop mais soyez prêt à accepter beaucoup ".

Mais absolument aucune raison raisonnable pour laquelle a getDataest une mauvaise chose, en fait, quelques personnes ont fait valoir que c'est beaucoup de sémantique, des getters aussi bien en soi, mais ne les nommez pas getX, pour moi, c'est au moins drôle .

Quelle est une chose, sans opinions, qui se brisera si j'utilise les getters de manière sensée et pour les données qui, de toute évidence, l'intégrité de l'objet ne se brise pas s'il le met hors tension?

Bien sûr, autoriser un getter pour une chaîne utilisée pour crypter quelque chose est au-delà de la stupidité, mais je parle des données dont votre système a besoin pour fonctionner. Peut-être que vos données sont tirées à travers un Providerde l'objet, mais, encore, l'objet doit toujours autoriser le Providerà faire un $provider[$object]->getData, il n'y a aucun moyen de le contourner.


Pourquoi je demande: Pour moi, les getters, lorsqu'ils sont utilisés de manière raisonnable et sur des données qui sont traitées comme "sûres" sont envoyés par Dieu, 99% de mes getters sont utilisés pour identifier l'objet, comme dans, je demande, par le biais du code Object, what is your name? Object, what is your identifier?, quiconque travaille avec un objet devrait savoir ces choses sur un objet, car presque tout ce qui concerne la programmation est une identité et qui d'autre sait mieux ce que c'est que l'objet lui-même? Je ne vois donc aucun problème réel, sauf si vous êtes un puriste.

J'ai regardé toutes les questions de StackOverflow sur "pourquoi les getters / setters" sont mauvais et bien que je convienne que les setters sont vraiment mauvais dans 99% des cas, les getters n'ont pas à être traités de la même manière juste parce qu'ils riment.

Un setter compromettra l'identité de votre objet et rendra très difficile le débogage de qui modifie les données, mais un getter ne fait rien.


2
Mon conseil est d'oublier la plupart de la théorie OO. Entraine toi. Écrivez beaucoup de code, puis maintenez-le. Vous en apprendrez beaucoup, beaucoup, beaucoup plus sur ce qui fonctionne bien et ce qui ne fonctionne pas en faisant quelque chose et en y revenant quelques mois plus tard.
jpmc26

1
@ jpmc26 Qu'est-ce que dans la mère de .... Je ne me suis jamais rendu compte qu'aucun de nous ne fait vraiment la POO correctement, j'ai toujours eu cette notion d'encapsulation strictement liée aux champs d'un objet, mais l'objet lui-même est un état et est en train d'être librement partagé. Vraiment, tout le monde fait mal la POO, alors, ou plutôt, la POO n'est pas possible si le paradigme est des objets. J'utilise la POO strictement pour exprimer comment un système fonctionne et je ne l'ai jamais traité comme le Saint Graal, ni SOLIDE. Ce ne sont que des outils que j'utilise principalement pour définir un concept raisonnablement bien développé (grâce à la mise en œuvre) de mon concept. Tout lit si c'est vrai?
coolpasta

2
--cont, plus j'écris, plus je me rends compte que la programmation consiste à être capable d'abstraire autant que nécessaire pour pouvoir raisonner, pour les autres, ainsi que pour vous sur votre base de code. Bien sûr, l'aspect technique consiste à s'assurer que vous effectuez les vérifications appropriées / définissez les bonnes règles. En vérité, celui qui réussit à faire mieux comprendre son code aux autres gagne. Assurez-vous que cela a du sens pour quiconque le lit, puis assurez-vous qu'il n'échoue pas en segmentant correctement les objets avec des interfaces, en vérifiant et en passant à d'autres paradigmes lorsqu'il est plus facile de les raisonner: soyez flexible.
coolpasta

2
Cela me semble être un homme de paille. Je ne crois pas qu'un développeur sérieux qui écrit réellement du code utilisable soutienne que l'utilisation judicieuse des getters est un problème. La question implique que les getters peuvent être utilisés judicieusement, ce qui implique en outre que les getters peuvent être raisonnables. Cherchez-vous quelqu'un pour dire qu'il n'y a pas d'utilisation judicieuse des getters? Vous attendez un moment.
JimmyJames

2
@coolpasta Vous avez raison. Je m'en fiche vraiment si c'est du pur OOP. Juste mieux. Donnez-moi le choix entre un code cassé qui peut être compris par les humains ou un code sans faille qui est indéchiffrable et je prendrai le code convivial à chaque fois. La POO ne m'est utile que lorsqu'elle réduit le nombre de choses auxquelles je dois penser à un moment donné. C'est la seule raison pour laquelle j'aime cette procédure plutôt simple. Sans cela, vous me faites simplement parcourir plusieurs fichiers de code source sans raison.
candied_orange

Réponses:


22

Vous ne pouvez pas écrire du bon code sans les getters.

La raison n'est pas parce que les getters ne cassent pas l'encapsulation, ils le font. Ce n'est pas parce que les getters ne tentent pas les gens de ne pas prendre la peine de suivre la POO, ce qui leur ferait mettre des méthodes avec les données sur lesquelles ils agissent. Ils font. Non, vous avez besoin de getters à cause des limites.

Les idées d'encapsulation et de conservation des méthodes avec les données sur lesquelles elles agissent ne fonctionnent tout simplement pas lorsque vous rencontrez une limite qui vous empêche de déplacer une méthode et vous oblige donc à déplacer des données.

C'est vraiment aussi simple que cela. Si vous utilisez des getters quand il n'y a pas de limite, vous finissez par ne pas avoir de vrais objets. Tout commence à tendre à la procédure. Ce qui fonctionne aussi bien que jamais.

La vraie POO n'est pas quelque chose que vous pouvez répandre partout. Cela ne fonctionne que dans ces limites.

Ces frontières ne sont pas minces. Ils contiennent du code. Ce code ne peut pas être OOP. Il ne peut pas non plus être fonctionnel. Non, ce code a nos idéaux dépouillés de sorte qu'il puisse faire face à la dure réalité.

Michael Fetters a appelé ce fascia de code après ce tissu conjonctif blanc qui maintient ensemble des sections d'une orange.

C'est une merveilleuse façon d'y penser. Cela explique pourquoi il est acceptable d'avoir les deux types de code dans la même base de code. Sans cette perspective, de nombreux nouveaux programmeurs s'accrochent durement à leurs idéaux, puis ont le cœur brisé et abandonnent ces idéaux lorsqu'ils atteignent leur première frontière.

Les idéaux ne fonctionnent qu'à leur place. Ne les abandonnez pas simplement parce qu'ils ne fonctionnent pas partout. Utilisez-les là où ils travaillent. Cet endroit est la partie juteuse que le fascia protège.

Un exemple simple de frontière est une collection. Cela contient quelque chose et n'a aucune idée de ce que c'est. Comment un concepteur de collection peut-il déplacer la fonctionnalité comportementale de l'objet détenu dans la collection alors qu'il n'a aucune idée de ce qu'il va contenir? Tu ne peux pas. Vous êtes contre une frontière. C'est pourquoi les collections ont des getters.

Maintenant, si vous le saviez, vous pourriez modifier ce comportement et éviter de bouger. Quand vous savez, vous devriez. Tu ne sais pas toujours.

Certaines personnes appellent cela simplement pragmatique. Et c'est. Mais c'est bien de savoir pourquoi nous devons être pragmatiques.


Vous avez dit que vous ne vouliez pas entendre d'arguments sémantiques et que vous sembliez préconiser de mettre des «getters sensés» partout. Vous demandez que cette idée soit contestée. Je pense que je peux montrer que l'idée a des problèmes avec la façon dont vous l'avez formulée. Mais je pense aussi que je sais d'où tu viens parce que j'y suis allé.

Si vous voulez des getters partout, regardez Python. Il n'y a pas de mot clé privé. Pourtant, Python fonctionne très bien. Comment? Ils utilisent une astuce sémantique. Ils nomment tout ce qui est censé être privé avec un soulignement de premier plan. Vous êtes même autorisé à en lire à condition d'en assumer la responsabilité. "Nous sommes tous des adultes ici", disent-ils souvent.

Alors, quelle est la différence entre cela et simplement mettre des getters sur tout en Java ou C #? Désolé mais c'est de la sémantique. Les conventions Pythons soulignent clairement que vous fouillez derrière la porte réservée aux employés. Slap getters sur tout et vous perdez ce signal. Avec la réflexion, vous auriez pu de toute façon dépouillé le privé et ne pas avoir perdu le signal sémantique. Il n'y a tout simplement aucun argument structurel à faire valoir ici.

Il nous reste donc à décider où accrocher le panneau "Employés uniquement". Qu'est-ce qui devrait être considéré comme privé? Vous appelez cela des «getters sensés». Comme je l'ai dit, la meilleure justification pour un getter est une frontière qui nous éloigne de nos idéaux. Cela ne devrait pas aboutir à des getters sur tout. Quand cela aboutit à un getter, vous devriez envisager de déplacer le comportement plus loin dans le morceau juteux où vous pouvez le protéger.

Cette séparation a donné lieu à quelques termes. Un objet de transfert de données ou DTO, ne possède aucun comportement. Les seules méthodes sont des getters et parfois des setters, parfois un constructeur. Ce nom est regrettable car ce n'est pas du tout un véritable objet. Les getters et setters ne sont en fait que du débogage de code qui vous donne un endroit pour définir un point d'arrêt. Si ce n'était pas pour ce besoin, ils ne seraient qu'un tas de champs publics. En C ++, nous les appelions des structs. La seule différence qu'ils avaient d'une classe C ++ était qu'ils étaient par défaut public.

Les DTO sont agréables parce que vous pouvez les jeter sur un mur d'enceinte et conserver vos autres méthodes en toute sécurité dans un bel objet de comportement juteux. Un vrai objet. Sans getters pour violer, c'est l'encapsulation. Mes objets comportementaux peuvent manger des DTO en les utilisant comme objets paramètres . Parfois, je dois en faire une copie défensive pour empêcher un état mutable partagé . Je ne diffuse pas de DTO mutables à l'intérieur de la partie juteuse à l'intérieur de la frontière. Je les résume. Je les cache. Et quand je rencontre enfin une nouvelle frontière, je tourne un nouveau DTO et je le jette sur le mur, ce qui en fait le problème de quelqu'un d'autre.

Mais vous voulez fournir des getters qui expriment l'identité. Eh bien, félicitations, vous avez trouvé une limite. Les entités ont une identité qui dépasse leur référence. Autrement dit, au-delà de leur adresse mémoire. Il faut donc le stocker quelque part. Et quelque chose doit pouvoir se référer à cette chose par son identité. Un getter qui exprime une identité est parfaitement raisonnable. Une pile de code qui utilise ce getter pour prendre des décisions que l'entité aurait pu prendre elle-même ne l'est pas.

En fin de compte, ce n'est pas l'existence de getters qui a tort. Ils sont bien meilleurs que les domaines publics. Ce qui est mauvais, c'est quand ils sont utilisés pour prétendre que vous êtes orienté objet alors que vous ne l'êtes pas. Les Getters sont bons. Être orienté objet est bon. Les getters ne sont pas orientés objet. Utilisez des getters pour tailler un endroit sûr pour être orienté objet.


C'est exactement pourquoi j'ai demandé et je ne suis pas sûr que ma conversation soit visible avec Robert, j'essayais également de découvrir un cas spécifique où cela me faisait clairement mal d'utiliser des "getters sensibles". Pour ma part, je ne suis pas d' accord avec les setters car au moins pour moi, il est très difficile d'attribuer la propriété à une action comme celle-ci, j'ai utilisé des setters dans tout et au moment où je me suis rendu compte que même 1 setter, lorsqu'il est affecté par trop d'objets tue moi ... je devais tout réécrire. L'utilisation judicieuse des getters, après des tests exhaustifs pendant si longtemps, n'a sérieusement aucun inconvénient si vous n'êtes pas un puriste.
coolpasta

1
@coolpasta vous pouvez créer des objets propriétaires de données. Les objets propriétaires de données sont des objets de données, mais ils sont conçus et nommés de manière à ce qu'ils soient (clairement) propriétaires des données. Bien sûr, ces objets auront des getters.
rwong

1
@rwong Qu'entendez-vous par clearly? Si des données me sont transmises par quelque chose, par valeur , clairement, mon objet possède maintenant les données, mais par sémantique, il ne le fait pas encore, à ce stade, il a simplement obtenu les données de quelqu'un d'autre. Il doit ensuite effectuer des opérations pour transformer ces données, les faire siennes, au moment où les données sont touchées, vous devez apporter des modifications sémantiques: vous avez nommé les données comme $data_from_requestet maintenant vous les avez opérées? Nommez-le $changed_data. Vous souhaitez exposer ces données à d'autres? Créez un getter getChangedRequestData, puis la propriété est clairement définie. Ou est-ce?
coolpasta

1
"Vous ne pouvez pas écrire du bon code sans les getters." C'est tout à fait faux, tout comme l'idée que les frontières ont intrinsèquement besoin de getters. Ce n'est pas du pragmatisme, juste de la paresse. Il est un peu plus facile de simplement jeter des données par-dessus la clôture et d'en finir, penser à des cas d'utilisation et concevoir une bonne API comportementale est difficile.
Robert Bräutigam

2
Excellente réponse!
cmaster - réintègre monica

10

Les Getters violent le principe d'Hollywood ("Ne nous appelez pas, nous vous appellerons")

Le principe d'Hollywood (alias Inversion of Control) stipule que vous n'appelez pas dans le code de bibliothèque pour faire avancer les choses; le framework appelle plutôt votre code. Parce que le framework contrôle les choses, la diffusion de son état interne à ses clients n'est pas nécessaire. Tu n'as pas besoin de savoir.

Dans sa forme la plus insidieuse, violer le principe d'Hollywood signifie que vous utilisez un getter pour obtenir des informations sur l'état d'une classe, puis que vous décidez des méthodes pour appeler cette classe en fonction de la valeur que vous obtenez. c'est une violation de l'encapsulation à son meilleur.

L'utilisation d'un getter implique que vous avez besoin de cette valeur, alors que ce n'est pas le cas.

Vous pourriez en fait avoir besoin de cette amélioration des performances

Dans les cas extrêmes d'objets légers qui doivent avoir les performances maximales possibles, il est possible (bien qu'extrêmement improbable) que vous ne puissiez pas payer la très faible pénalité de performance imposée par un getter. Cela n'arrivera pas 99,9% du temps.


1
Je comprends et je volerai avec ta vérité, que je n'ai pas besoin de savoir. . Mais j'ai atteint un endroit où je dois. J'ai un Generatorobjet qui parcourt tous mes Itemsobjets, puis appelle getNamede chacun Itempour faire quelque chose de plus. Quel est le problème avec ça? Puis, en retour, le Generatorcrache des chaînes formatées. C'est dans mon cadre, pour lequel j'ai ensuite une API en plus que les gens peuvent utiliser pour exécuter tout ce que les utilisateurs fournissent mais sans toucher au cadre.
coolpasta

3
What is the issue with this?Aucun que je puisse voir. C'est essentiellement ce que fait une mapfonction. Mais ce n'est pas la question que vous avez posée. Vous avez essentiellement demandé: «Existe-t-il des conditions dans lesquelles un getter peut être déconseillé». J'ai répondu avec deux, mais cela ne signifie pas que vous abandonnez complètement les colons.
Robert Harvey

1
Vous posez cette mauvaise question au mauvais gars. Je suis pragmatique; Je fais tout ce qui convient le mieux à mes programmes spécifiques, et je ne mets pas beaucoup de stock dans les "principes" à moins qu'ils ne servent mes objectifs.
Robert Harvey

2
@ jpmc26: Frameworks. Pas des bibliothèques.
Robert Harvey

2
Un framework est une tentative de transformer un langage à usage général en un langage spécifique à un domaine. Une bibliothèque est une collection d'algorithmes réutilisables. Soit on vous demandera d'en dépendre. C'est à vous de décider si vous voulez coder en dur la dépendance ou rendre la dépendance facilement substituable.
candied_orange

2

Détails de mise en œuvre des fuites Getters et rupture de l'abstraction

Considérer public int millisecondsSince1970()

Vos clients penseront "oh, c'est un int" et il y aura des milliers d'appels en supposant que c'est un int , en faisant des mathématiques entières en comparant les dates, etc ... Quand vous réaliserez que vous avez besoin d'un long , il y aura beaucoup du code hérité avec des problèmes. Dans un monde Java, vous ajouteriez @deprecatedà l'API, tout le monde l'ignorerait et vous êtes bloqué en maintenant un code obsolète et bogué. :-( (je suppose que les autres langues sont similaires!)

Dans ce cas particulier, je ne sais pas quelle meilleure option pourrait être, et la réponse @candiedorange est complètement sur le point, il y a plusieurs fois où vous avez besoin de getters, mais cet exemple illustre les inconvénients. Chaque getter public a tendance à vous "enfermer" dans une certaine implémentation. Utilisez-les si vous avez vraiment besoin de «franchir les frontières», mais utilisez-les le moins possible et avec soin et prévoyance.


C'est vrai, mais quelle est la solution de modèle pour appliquer un type / structure de données à la variable d'un objet, ainsi qu'à tout ce qui utilise le getter pour cette variable?
coolpasta

1
Cet exemple illustre un phénomène différent, l'obsession primitive. De nos jours, la plupart des bibliothèques et des frameworks ont des classes qui représentent timepointet timespanrespectivement. ( epochest une valeur particulière de timepoint.) Sur ces classes, ils fournissent des getters tels que getIntor getLongou getDouble. Si les classes qui manipulent le temps sont écrites de manière à (1) déléguer l'arithmétique liée au temps à ces objets et méthodes, et (2) donner accès à ces objets temporels via des getters, le problème est résolu, sans avoir besoin de se débarrasser des getters .
rwong

1
Pour résumer, les getters font partie de l'API, et par conséquent les getters doivent être conçus avec des considérations suffisantes pour toutes les conséquences, y compris les conséquences passées, actuelles et futures. Mais cela ne mène pas à la conclusion d'éviter ou de minimiser le nombre de getters. En fait, s'il y avait un besoin futur d'accéder à une information particulière pour laquelle un getter a été omis, cela aurait nécessité un changement d'API côté bibliothèque et un changement de code. Ainsi, la décision de savoir si un getter particulier doit être mis en œuvre ou évité doit être basée sur le domaine et l'intention, et non sur une raison préventive.
rwong

1
"millisecondsSince1970" a cessé de fonctionner pour les bits 32 bits avant la fin de janvier 1970, donc je doute qu'il y aura beaucoup de code qui l'utilisera :-)
gnasher729

1
@rwong Un peu correct, mais vous supposez que les bibliothèques préférées qui représentent les points temporels sont stables. Ce qu'ils ne sont pas. Par exemple moment.js
user949300

1

Je pense que la première clé est de se rappeler que les absolus ont toujours tort.

Envisagez un programme de carnet d'adresses, qui stocke simplement les coordonnées des personnes. Mais, faisons-le bien et divisons-le en couches contrôleur / service / référentiel.

Il y aura une Personclasse qui contient des données réelles: nom, adresse, numéro de téléphone, etc. Addresset qui Phonesont également des classes, afin que nous puissions utiliser le polymorphisme plus tard pour prendre en charge les schémas non américains. Ces trois classes sont des objets de transfert de données , elles ne seront donc que des getters et des setters. Et, cela a du sens: ils ne vont pas avoir de logique en dehors de l'emporter equalset getHashcode; leur demander de vous donner une représentation des informations d'une personne complète n'a pas de sens: est-ce pour une présentation HTML, une application GUI personnalisée, une application console, un point de terminaison HTTP, etc.?

C'est là que le contrôleur, le service et le référentiel entrent en jeu. Toutes ces classes vont être logiques et légères, grâce à l'injection de dépendances.

Le contrôleur va obtenir un service injecté, qui exposera des méthodes comme getet save, qui prendront toutes les deux des arguments raisonnables (par exemple, getpourraient avoir des remplacements pour rechercher par nom, rechercher par code postal, ou tout simplement obtenir tout; saveprendra probablement un seul Personet enregistrez-le). Quels getters aurait le contrôleur? La possibilité d'obtenir le nom de la classe concrète peut être utile pour la journalisation, mais quel état tiendra-t-il autre que l'état qui lui a été injecté?

De même, la couche de service obtiendra un référentiel injecté, donc elle peut réellement obtenir et persister Person, et elle pourrait avoir une certaine "logique métier" (par exemple, nous allons peut-être exiger que tous les Personobjets aient au moins une adresse ou un téléphone nombre). Mais, encore une fois: dans quel état cet objet a-t-il autre que celui injecté? Encore une fois, aucun.

La couche de référentiel est l'endroit où les choses deviennent un peu plus intéressantes: elle va établir une connexion avec un emplacement de stockage, par exemple. un fichier, une base de données SQL ou un magasin en mémoire. Il est tentant, ici, d'ajouter un getter pour obtenir le nom de fichier ou la chaîne de connexion SQL, mais c'est cet état qui a été injecté dans la classe. Le référentiel doit-il avoir un getter pour indiquer s'il est correctement connecté à son magasin de données? Eh bien, peut-être, mais à quoi servent ces informations en dehors de la classe de référentiel elle-même? La classe de référentiel elle-même devrait pouvoir tenter de se reconnecter à son magasin de données si nécessaire, ce qui suggère que la isConnectedpropriété a déjà une valeur douteuse. De plus, une isConnectedpropriété va probablement se tromper au moment où elle aurait le plus besoin d'être correcte: vérificationisConnectedavant d'essayer d'obtenir / stocker des données ne garantit pas que le référentiel sera toujours connecté lorsque l'appel "réel" est effectué, il ne supprime donc pas la nécessité de gérer les exceptions quelque part (il existe des arguments pour savoir où cela devrait aller, au-delà la portée de cette question).

Pensez également aux tests unitaires (vous écrivez des tests unitaires, n'est-ce pas?): Le service ne s'attend pas à ce qu'une classe spécifique soit injectée, il va plutôt s'attendre à ce qu'une implémentation concrète d'une interface soit injectée. Cela permet à l'application dans son ensemble de changer l'endroit où les données sont stockées sans rien faire d'autre que d'échanger le référentiel injecté dans le service. Il permet également au service d'être testé unitaire en injectant un référentiel factice. Cela signifie penser en termes d'interfaces plutôt que de classes. L'interface du référentiel exposerait-elle des getters? Autrement dit, existe-t-il un état interne qui serait: commun à tous les référentiels, utile en dehors du référentiel et non injecté dans le référentiel? J'aurais du mal à penser à tout moi-même.

TL; DR

En conclusion: à part les classes dont le seul but est de transporter des données, rien d'autre dans la pile n'a de place pour mettre un getter: l'état est soit injecté soit hors de propos en dehors de la classe ouvrière.


Je trouve cette ligne de pensée très similaire au débat sur la bonne utilisation des propriétés C #. En bref, les propriétés (qui peuvent être getter-only ou getter-setter-pair) sont censées respecter un certain contrat, comme "ce que vous définissez est ce que vous obtenez", "getter devrait être principalement sans effets secondaires", "getter devrait éviter de lancer des erreurs", etc. Contrairement à votre conclusion, il existe de nombreux états d'objet qui peuvent être exposés en tant que propriétés (ou getters). Les exemples sont trop nombreux pour être cités ici.
rwong

2
@rwong Je serais intéressé de connaître quelques exemples, disons pour une application "normale" "entreprise" implémentée par une seule équipe. Supposons également, par souci de simplicité, qu'il n'y a pas de tiers impliqués. Vous savez, un système autonome, comme un calendrier, une animalerie, tout ce que vous voulez.
Robert Bräutigam

1

Quelle est une chose, sans opinions, qui se brisera si j'utilise les getters de manière sensée et pour les données qui, de toute évidence, l'intégrité de l'objet ne se brise pas s'il le met hors tension?

Membres mutables.

Si vous avez une collection de choses dans un objet que vous exposez via un getter, vous vous exposez potentiellement à des bogues associés aux mauvaises choses ajoutées à la collection.

Si vous avez un objet mutable que vous exposez via un getter, vous vous ouvrez potentiellement à l'état interne de votre objet modifié d'une manière que vous ne vous attendez pas.

Un très mauvais exemple serait un objet qui compte de 1 à 100 exposant sa valeur actuelle comme référence mutable. Cela permet à un objet tiers de modifier la valeur de Current de telle sorte que la valeur puisse se trouver en dehors des limites attendues.

C'est principalement un problème avec l'exposition des objets mutables. L'exposition de structures ou d'objets immuables n'est pas du tout un problème, car toute modification de ceux-ci modifiera une copie à la place (généralement soit par une copie implicite dans le cas d'une structure, soit par une copie explicite dans le cas d'un objet immuable).

Pour utiliser une métaphore qui pourrait faciliter la compréhension de la différence.

Une fleur a un certain nombre de choses uniques à son sujet. Il a un Coloret il a un certain nombre de pétales (NumPetals). Si j'observe cette fleur, dans le monde réel, je peux clairement voir sa couleur. Je peux alors prendre des décisions basées sur cette couleur. Par exemple, si la couleur est noire, ne donnez pas à la petite amie mais si la couleur est rouge, donnez-la à la petite amie. Dans notre modèle d'objet, la couleur de la fleur serait exposée comme un getter sur l'objet fleur. Mon observation de cette couleur est importante pour les actions que je vais effectuer, mais elle n'a aucun impact sur l'objet fleur. Je ne devrais pas pouvoir changer la couleur de cette fleur. De même, il n'est pas logique de masquer la propriété color. Une fleur ne peut généralement pas empêcher les gens d'observer sa couleur.

Si je teinte la fleur, je devrais appeler la méthode ReactToDye (Color dyeColor) sur la fleur, qui changera la fleur selon ses règles internes. Je peux alors interroger à Colornouveau la propriété et réagir à tout changement après la méthode ReactToDye a été appelée. Il serait erroné pour moi de modifier directement le Colorde la fleur et si je pouvais alors l'abstraction est tombée en panne.

Parfois (moins fréquemment, mais toujours assez souvent pour que cela mérite d'être mentionné) les setters sont une conception OO tout à fait valide. Si j'ai un objet Client, il est tout à fait valable d'exposer un setter pour son adresse. Que vous appeliez cela setAddress(string address)ou ChangeMyAddressBecauseIMoved(string newAddress)simplement, string Address { get; set; }c'est une question de sémantique. L'état interne de cet objet doit changer et la manière appropriée de le faire est de définir l'état interne de cet objet. Même si j'ai besoin d'un enregistrement historique des adresses dans lesquelles Customerj'ai vécu, je peux utiliser le setter pour changer mon état interne de manière appropriée. Dans ce cas, il n'est pas logique que le Customersoit immuable et il n'y a pas de meilleur moyen de changer une adresse que de fournir un setter pour le faire.

Je ne sais pas qui suggère que les Getters et les setters sont mauvais ou brisent les paradigmes orientés objet, mais s'ils le font, ils le font probablement en réponse à une caractéristique de langage spécifique de la langue qu'ils utilisent. J'ai le sentiment que cela est né de la culture Java "tout est un objet" (et s'est peut-être étendu à d'autres langages). Le monde .NET n'a pas du tout cette discussion. Les Getters et Setters sont une fonctionnalité de langage de première classe qui est utilisée non seulement par les personnes écrivant des applications dans la langue, mais également par l'API de langue elle-même.

Vous devez être prudent lorsque vous utilisez des getters. Vous ne voulez pas exposer les mauvais éléments de données et vous ne voulez pas non plus exposer les données de la mauvaise manière (c'est-à-dire des objets mutables, des références à des structures qui peuvent permettre à un consommateur de modifier l'état interne). Mais vous voulez modéliser vos objets afin que les données soient encapsulées de telle manière que vos objets puissent être utilisés par des consommateurs externes et protégés contre toute utilisation abusive par ces mêmes consommateurs. Cela nécessitera souvent des getters.

Pour résumer: les Getters et Setters sont des outils de conception orientés objet valides. Ils ne doivent pas être utilisés si généreusement que tous les détails de l'implémentation sont exposés, mais vous ne voulez pas non plus les utiliser avec parcimonie que les consommateurs d'un objet ne peuvent pas utiliser l'objet efficacement.


-1

Quelle est une chose, sans opinions, qui se brisera si j'utilise des getters ...

La maintenabilité se brisera.

Chaque fois (je devrais dire presque toujours) que vous offrez un getter que vous donnez plus que ce qui vous a été demandé. Cela signifie également que vous devez maintenir plus que ce qui était demandé, ergo vous diminuez la maintenabilité sans raison réelle.

Allons avec l' Collectionexemple de @ candied_orange . Contrairement à ce qu'il écrit, Collection il ne devrait pas y avoir de getter. Les collections existent pour des raisons très spécifiques, notamment pour itérer sur tous les articles. Cette fonctionnalité n'est une surprise pour personne, donc elle devrait être implémentée dans le Collection, au lieu de pousser ce cas d'utilisation évident sur l'utilisateur, en utilisant des cycles et des getters ou autres joyeusetés.

Pour être juste, certaines limites ne getters besoin. Cela se produit si vous ne savez vraiment pas à quoi ils serviront. Par exemple, vous pouvez obtenir par programme la trace de pile de la Exceptionclasse Java de nos jours, parce que les gens voulaient l'utiliser pour toutes sortes de raisons curieuses que les auteurs n'avaient pas ou ne pouvaient pas imaginer.


1
Contre-exemple. Considérons une fonction qui doit répéter deux Collections simultanément, comme dans (A1, B1), (A2, B2), .... Cela nécessite une implémentation de "zip". Comment implémentez-vous "zip" si la fonction itération est implémentée sur l'un des deux Collection- comment accédez-vous à l'élément correspondant dans l'autre?
rwong

@rwong Le Collectiondoit s'implémenter zipavec lui-même, selon la façon dont la bibliothèque est écrite. Si ce n'est pas le cas, vous l'implémentez et soumettez une demande d'extraction au créateur de la bibliothèque. Notez que j'ai convenu que certaines limites ont parfois besoin de getters, mon vrai problème est que les getters sont partout de nos jours.
Robert Bräutigam

Dans ce cas, cela implique que la bibliothèque doit avoir des getters sur l' Collectionobjet, au moins pour sa propre implémentation de zip. (C'est-à-dire que le getter doit avoir au moins la visibilité du paquet.) Et maintenant, vous
dépendez

Je ne te comprends pas. De Collectiontoute évidence, il peut accéder à son propre état interne, ou pour être plus précis, n'importe quelle Collectioninstance peut accéder à l'état interne de n'importe quel autre. C'est le même type, pas besoin de getters.
Robert Bräutigam

Je vous entends aussi, mais là encore, quelle est exactement la meilleure solution aux getters? Je sais que cela sort du cadre de la question.
coolpasta
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.