Quel est l'intérêt des propriétés?

11

Voici quelques arguments pour les propriétés et mes contre-arguments:

Plus facile à utiliser que d'écrire des méthodes getter et setter

Les paires de méthodes getter et setter sont une odeur de code. Les rendre plus faciles à écrire, c'est comme faciliter l'échec d'un test de mathématiques en utilisant un formulaire Scantron et en remplissant tous les «C». Les objets qui contiennent uniquement un état, pour la persistance, ne doivent pas utiliser de getters / setters et doivent créer des objets immuables au moment de la persistance.

Ce qui est important pour un consommateur d'un objet, c'est ce qu'il fait, pas comment il le fait. Son comportement est ce qu'il fait; son état est de savoir comment il le fait. Si vous vous souciez de l'état d'un objet (sauf pour la persistance, bien que cela casse également OO), vous ne faites tout simplement pas de POO et ne perdez pas ses avantages.

Ils donnent une indication approximative des performances aux consommateurs

C'est quelque chose qui pourrait changer à l'avenir, pour n'importe quelle propriété donnée. Supposons que dans la version 1.0, l'accès à PropertyX renvoie simplement un champ. Dans la version 1.5, si le champ est null, PropertyX utilise le modèle Null Object pour créer un nouvel objet null. Dans la version 2.0, le champ est encore validé par la méthode getter dans PropertyX.

Comme la propriété devient de plus en plus complexe, l'indication de performance d'utilisation d'une propriété semble de moins en moins véridique.

Ils sont meilleurs que les champs publics

C'est vrai. Mais les méthodes aussi.

Ils représentent un aspect fondamentalement différent d'un objet qu'une méthode, et tous les consommateurs de l'objet devraient s'en soucier

Êtes-vous sûr que les deux affirmations ci-dessus sont vraies?

Ils sont plus faciles à taper, mec

Bien sûr, la frappe myObject.Lengthest plus facile que la frappe myObject.Length(), mais cela ne pourrait-il pas être corrigé avec un peu de sucre syntaxique?


Pourquoi utiliser des méthodes plutôt que des propriétés?

  • Aucune garantie de performance. L'API restera véridique même si la méthode devient plus complexe. Le consommateur devra profiler son code s'il rencontre des problèmes de performances et ne pas s'appuyer sur Word-of-API.

  • Moins de réflexion pour le consommateur. Cette propriété a-t-elle un setter? Une méthode, bien sûr.

  • Le consommateur pense d'un état d'esprit approprié. En tant que consommateur d'une API, je souhaite interagir avec le comportement d'un objet. Quand je vois des propriétés dans l'API, cela ressemble beaucoup à l'état. En fait, si les propriétés en font trop, elles ne devraient même pas être des propriétés, donc vraiment, les propriétés dans un état API ARE telles qu'elles apparaissent aux consommateurs.

  • Le programmeur de l'API réfléchira plus profondément aux méthodes avec des valeurs de retour et évitera si possible de modifier l'état de l'objet dans de telles méthodes. La séparation des commandes des requêtes doit être appliquée dans la mesure du possible.


Je vous demande donc pourquoi utiliser des propriétés plutôt que des méthodes? La plupart des points sur MSDN sont des odeurs de code en soi et n'appartiennent ni aux propriétés ni aux méthodes.

(Ces pensées me sont venues après avoir pensé au CQS.)

xofz
la source
5
+1. Les propriétés sont des méthodes avec la syntaxe des champs et cela n'a tout simplement aucun sens.
Nemanja Trifunovic
23
@Nemanja Trifunovic: C'est parfaitement logique si vous avez écrit type GetFoo() void SetFoo()dix mille fois. Pendant tout mon temps à écrire du code C #, je n'ai jamais été dérouté par une propriété.
Ed S.
23
Il me semble que vous avez déjà pris votre décision. Quelle est la question?
Dean Harding
9
@Nemanja Trifunovic: "Les getters et setters sont généralement mauvais" Vous pouvez répéter cela autant de fois que vous le souhaitez, mais cela ne le rendra pas vrai. Que diriez-vous d'expliquer pourquoi vous pensez qu'ils sont mauvais? Dans la mesure où SO plante en raison de la saisie du nom de la propriété au lieu du nom de la variable sous-jacente, il s'agit d'une erreur impossible à manquer, car cela bloquera votre programme dès que vous appelez la propriété et c'est assez rare. Quand cela arrive, je sais exactement ce que j'ai fait pour le provoquer et je l'ai réparé en deux secondes. Je ne vois pas le problème.
Ed S.
3
Juste en passant pour souligner à @NemanjaTrifunovic que cet article Pourquoi les méthodes getter et setter sont mauvaises est assez ancien , concerne Java et souligne une faiblesse java : la faiblesse de ne pas avoir de propriétés . (montrant ainsi trop de détails de mise en œuvre)… se mordant la queue ici. (il devrait être intitulé les getters et setters sont mauvais en java, car nous n'avons pas de norme bien pensée à ce sujet )
ZJR

Réponses:

44

Pourquoi s'agit-il de "méthodes contre propriétés"? Une méthode fait quelque chose. Une propriété est, bien, un membre d'un objet. Ce sont des choses totalement différentes, bien que deux types de méthodes couramment écrites - les getters et les setters - correspondent aux propriétés. Étant donné que la comparaison des propriétés avec les méthodes en général n'a pas de sens, je suppose que vous vouliez parler des getters / setters.

Les paires de méthodes getter et setter sont une odeur de code.

Pas nécessairement, je dirais, mais de toute façon ce n'est pas une question de getters / setters vs propriétés, mais une question de savoir si l'un doit être utilisé. Notez également que vous pouvez par exemple omettre la setXpièce comme vous pouvez créer des propriétés en lecture seule.

Ce qui est important pour un consommateur d'un objet, c'est ce qu'il fait, pas comment il le fait. Son comportement est ce qu'il fait; son état est de savoir comment il le fait. Si vous vous souciez de l'état d'un objet (sauf pour la persistance, bien que cela casse également OO), vous ne faites tout simplement pas de POO et ne perdez pas ses avantages.

Une attitude très discutable. Si l'interface graphique veut sortir des données détenues par un DataStuffManagerImpl, elle doit en obtenir ce numéro d'une certaine manière (et non, entasser la moitié de l'application dans la classe de widget n'est pas une option).

Comme la propriété devient de plus en plus complexe, l'indication de performance d'utilisation d'une propriété semble de moins en moins véridique.

[Les méthodes ont] Aucune garantie de performance. L'API restera véridique même si la méthode devient plus complexe. Le consommateur devra profiler son code s'il rencontre des problèmes de performances et ne pas s'appuyer sur Word-of-API.

Dans presque tous les cas, toute la logique de validation, etc. est toujours effectivement O (1), ou autrement négative en termes de coût. Sinon, vous êtes peut-être allé trop loin et il est temps de changer. Et une getX()méthode est généralement considérée comme aussi bon marché!

Bien sûr, taper myObject.Length est plus facile que taper myObject.Length (), mais cela ne pourrait-il pas être corrigé avec un peu de sucre syntaxique?

Les propriétés sont ce sucre syntaxique.

Moins de réflexion pour le consommateur. Cette propriété a-t-elle un setter? Une méthode, bien sûr.

"Y a-t-il un passeur pour ce getter?" Même différence.

Le programmeur de l'API réfléchira plus profondément aux méthodes avec des valeurs de retour et évitera si possible de modifier l'état de l'objet dans de telles méthodes. La séparation des commandes des requêtes doit être appliquée dans la mesure du possible.

Je ne sais pas ce que vous essayez de nous dire ici. Pour les propriétés, il existe une loi non écrite encore plus forte pour ne pas provoquer d'effets secondaires.

ChrisF
la source
3
+1, et ce n'est pas une loi non écrite. Les directives d'utilisation de la propriété sur MSDN l'ont écrit avec beaucoup d'autres. Je pense que OP devrait les parcourir pour se débarrasser de nombreux doutes. Et je ne peux pas poster de lien ici puisque j'écris ceci depuis mon téléphone, mais les directives devraient être faciles à trouver.
décyclone
@delnan: Les propriétés ne sont pas que du sucre syntaxique. Les propriétés peuvent être traitées comme des champs (elles peuvent être utilisées comme cible d'affectation, si elles ont un setter). Les méthodes ne le peuvent pas.
xofz
1
@SamPearson: obj.x = ycorrespond à obj.setX(y)et obj.xcorrespond à obj.getX(). En quoi est-ce différent?
1
@SamPearson vous pouvez changer la visibilité d'un setter de propriété et d'un getter indépendamment l'un de l'autre. Je l'utilise tout le temps avec mes objets Entity Framework: mes setters sont protégés (donc seul le framework peut définir une valeur). Il est donc parfaitement viable de dire int length = myObject.Length sans autoriser myObject.Length = 10
Michael Brown
19

Les paires de méthodes getter et setter sont une odeur de code.

C'est une hypothèse erronée et complètement incorrecte. Les méthodes Get / Set sont un moyen d'encapsuler un membre de données et ne sont en aucun cas une "odeur de code". Je déteste vraiment la façon dont certaines personnes utilisent le terme "odeur de code", mais de toute façon ...

C'est quelque chose qui pourrait changer à l'avenir, pour n'importe quelle propriété donnée. Supposons que dans la version 1.0, l'accès à PropertyX renvoie simplement un champ. Dans la version 1.5, si le champ est null, PropertyX utilise le modèle Null Object pour créer un nouvel objet null. Dans la version 2.0, le champ est encore validé par la méthode getter dans PropertyX.

Comme la propriété devient de plus en plus complexe, l'indication de performance d'utilisation d'une propriété semble de moins en moins véridique.

Cela ressemble à une API mal conçue, je ne vois pas comment c'est la faute de la syntaxe des propriétés.

Les propriétés en C # étaient simplement du sucre syntaxique pour un modèle très courant qui nous facilitait un peu la vie en tant que programmeurs. Cependant, de nos jours, si vous souhaitez utiliser la liaison de données, vous "devez" utiliser des propriétés, elles sont donc un peu plus que du sucre syntaxique. Je suppose que je ne suis vraiment pas d'accord avec aucun de vos points et je ne comprends pas pourquoi vous n'aimez pas une fonctionnalité de langue qui prend directement en charge un modèle commun.

Ed S.
la source
1
Je vais donner une prime pour trouver un meilleur terme que l'odeur de code. Il suffit de lire un livre sur la refactorisation où il est utilisé environ mille fois. C'est comme des ongles sur un tableau noir.
JeffO
1
@Jeff O: Oui, je trouve que les gens qui semblent utiliser ce terme le plus souvent n'ont pas beaucoup d'expérience.
Ed S.
6
@Jeff O et @Ed S: IME, "odeur de code" est devenu le terme de référence que les gens utilisent lorsqu'ils veulent vraiment dire "je n'aime pas ça, mais je n'ai en fait aucune raison"
Steven Evers
3
@SnOrfus: Oui, ou lorsqu'ils ont des opinions "religieuses" qui n'ont souvent guère de sens dans la pratique, comme "une méthode ne devrait jamais avoir plus de X nombre de lignes" , qui à son tour n'est généralement qu'une répétition de quelque chose qu'ils lisent quelque part.
Ed S.
1
L' utilisation inappropriée des méthodes getter / setter est une odeur de code, mais il devrait en être de même de l'utilisation inappropriée de quoi que ce soit . Même lorsque les choses ont des cas d'utilisation étroits, leur utilisation pour ces cas d'utilisation ne doit pas être considérée comme une odeur de code. La création de setters pour tout ne doit pas être une habitude aveugle, mais il ne faut pas avoir peur de les inclure le cas échéant. Ce qui est nécessaire est d'équilibrer l'utilité pour les clients de pouvoir changer d'état, par rapport à être en mesure de découvrir à l'avenir ce qu'était l'état plus tôt (facile si l'état est immuable). Les deux sont utiles, mais le code doit en choisir un.
supercat
10

Votre prémisse est fausse.

Les propriétés ne sont en aucun cas un contrat de performance ou d'implémentation interne ou autre.
Les propriétés sont des paires de méthodes spéciales, qui représentent sémantiquement un attribut, si vous voulez. Et donc le seul contrat est qu'une propriété se comporte comme vous vous attendez à ce qu'un attribut se comporte.
Si je demande la couleur d'un objet, je ne fais pas d'hypothèse si elle est obtenue par un simple accès au champ, ou si elle est calculée juste à temps.
Le tout est d'avoir la possibilité d'exposer le comportement d'un attribut à l'aide de méthodes, tout en étant transparent pour le consommateur de l'interface, que vous utilisiez un champ ou des accesseurs.

Avoir des attributs (et masquer leur implémentation, comme les propriétés s'opposent aux champs) est une puissante fonctionnalité déclarative , qui est la base pour avoir des inspecteurs d'objets visuels et d'autres génération de vues automatiques, pour la liaison de données et bien d'autres choses utiles. Et surtout, il transporte également clairement ces informations pour vos collègues programmeurs.

back2dos
la source
6

Le consommateur pense d'un état d'esprit approprié. En tant que consommateur d'une API, je souhaite interagir avec le comportement d'un objet. Quand je vois des propriétés dans l'API, cela ressemble beaucoup à l'état. En fait, si les propriétés en font trop, elles ne devraient même pas être des propriétés, donc vraiment, les propriétés dans un état API ARE telles qu'elles apparaissent aux consommateurs.

Une propriété est censée être un état. Si le code setter ou getter changements sous - jacents à ajouter de manière significative au traitement requis pour définir ou obtenir l'état alors vraiment il n'est plus une propriété et vous devrait le remplacer par des méthodes. Cela dépend de vous en tant que développeur du code.

Mettre trop (combien coûte trop dépend) du code dans un setter ou un getter est faux, mais ce n'est pas parce que c'est possible de jeter le motif dans tous les cas.

ChrisF
la source
6

Une chose qui vous manque, et je ne sais pas si cela est dû à l'ignorance ou si vous négligez intentionnellement ce détail, c'est que les propriétés SONT des méthodes.

Lorsque vous utilisez la syntaxe de propriété de C #, le compilateur crée silencieusement des méthodes getter et setter avec des métadonnées qui indiquent aux langages qui comprennent les propriétés comme une fonctionnalité syntaxique de première classe de les traiter comme telles. C'est en fait le sucre syntaxique que vous demandez. Quelques langages qui peuvent être exécutés sur le CLR ne vous cachent pas complètement ce détail (Boo, si la mémoire me sert, et certaines implémentations Lisp), et vous pouvez appeler explicitement les getters et setters.

Les propriétés ont parfois des effets secondaires, comme déclencher des événements de notification de changement (INotifyPropertyChanged, par exemple) ou marquer une instance d'une classe comme sale. C'est là qu'ils ont leur vraie valeur, bien que les créer soit un peu plus de travail (sauf dans Boo, qui a des macros syntaxiques).

Maintenant, la question plus large est de savoir si les méthodes getter et setter sont, en fait, une bonne chose. Il y a clairement des compromis; si vous avez des méthodes de mutation, votre code est intrinsèquement moins parallélisable, car vous devez maintenant faire face aux problèmes de synchronisation des threads. Et il est tout à fait possible que vous risquiez de violer la loi de Demeter en utilisant des méthodes de mutation (qu'elles soient appelées propriétés ou méthodes getter / setter; elles sont équivalentes), car c'est tellement pratique de le faire.

Mais parfois, c'est la bonne chose à faire. Parfois, votre problème consiste à récupérer des données d'une source, à les modifier un peu, à présenter les données mises à jour à l'utilisateur et à enregistrer les modifications. Cet idiome est bien servi par les propriétés. Comme le dit l' article d'Allen Holub , ce n'est pas parce que les getters et les setters ont des problèmes que vous ne devriez jamais les utiliser. (Guruspeak abstrait perroquet considéré comme nuisible). Même lorsque vous suivez l'approche Classe / Responsabilités / Collaborateurs, vous finissez toujours par exposer des informations ou des présentations d'informations à quelqu'un; il s'agit de minimiser la surface d'enchevêtrement.

L'avantage des propriétés est qu'il est très facile de faire en sorte qu'un autre objet en profite. L'inconvénient des propriétés est qu'il est très facile qu'un autre objet en profite, et vous ne pouvez pas facilement empêcher cela.

La mutation d'objet est logique au niveau de la couche contrôleur, dans, par exemple, une architecture MVC. Voilà votre collaborateur. Votre point de vue est également un collaborateur, mais il ne doit pas directement muter vos objets, car il a des responsabilités différentes. Rouler le code de présentation dans vos objets métier n'est pas nécessairement la bonne façon d'éviter les getters / setters.

L'équation change un peu si vous utilisez des abstractions fonctionnelles plutôt que des OO pures, ce qui rend généralement la copie d'un objet "record" avec quelques changements assez banale, mais pas gratuite. Et bon, si vous essayez d'obtenir des garanties de performances, les propriétés sont généralement plus prévisibles que la reconstruction paresseuse d'une collection immuable.

JasonTrue
la source
5

Une autre raison importante de l'utilisation de propriétés dans mon livre est qu'elles peuvent augmenter considérablement la lisibilité.

account.setBalance(Account.getBalance()+deposit);

est nettement moins lisible que

account.Balance += deposit;
apoorv020
la source
6
Sauf que Balance ne doit pas être une propriété car il y a certainement une logique métier importante associée à sa modification (vérifier le découvert; facturer les frais de service; enregistrer la transaction, etc.) Votre point de lisibilité est complètement valide s'il est appliqué à un autre exemple ( Temperature, BackgroundColour etc.)
Kate Gregory
9
Pourquoi pas account.Deposit (montant)?
xofz
4
@Sam Pearson: +1. Un exemple parfait de pourquoi les getters / setters indiquent souvent une mauvaise conception.
Nemanja Trifunovic
4

Vous avez presque le point de vue religieux opposé à moi :)

Quand j'ai déménagé de Delphi à Java, le manque de propriétés a été un énorme affront pour moi. J'avais l'habitude de déclarer une propriété et de la pointer directement vers une variable privée ou d'écrire un getter & setter (protégé), puis de les "raccorder" à la propriété. Cela a encapsulé la propriété et contrôlé la façon dont elle devrait être accessible de manière claire et sans ambiguïté. Vous pouvez avoir une propriété en lecture seule qui calcule le total lors de l'accès et l'appeler "Total" et non "_getTotal ()" ou des ballochs similaires. Cela signifiait également que vous pouviez restreindre l'accès aux propriétés en les rendant protégées dans la classe de base abstraite, puis en les déplaçant vers le public ou publiées dans des sous-classes.

Pour moi, les propriétés sont un moyen plus naturel et expressif de définir et d'obtenir l'état de l'objet. S'appuyer sur des conventions de codage non appliquées est plus une "odeur de code" que tout ce dont vous critiquez les propriétés. En outre, comme d'autres l'ont dit, proposer une utilisation mal conçue des propriétés et utiliser ses échecs pour essayer de rejeter le concept de propriétés en général est une forme extrêmement mauvaise. Il est possible que vous n'ayez vu que la mauvaise utilisation des propriétés et supposez que c'est comme ça, je suppose, mais vous devriez faire plus de recherches avant de devenir trop dogmatique.

mcottle
la source
+1 C'est en effet une véritable utilisation d'une propriété. Très bonne réponse. " Cela signifiait également que vous pouviez restreindre l'accès aux propriétés en les rendant protégées dans la classe de base abstraite, puis en les déplaçant vers le public ou publiées dans des sous-classes. "
Karthik Sreenivasan
Delphi abuse des propriétés ad nauseam. Vous ne triez pas la liste, vous définissez sa sorted propriété sur true. Donc, le régler sur falsevous donne la liste d'origine. : D Ou faut-il le mélanger au hasard? : D Ou peu importe, c'est juste stupide. De plus, c'est sacrément lent par rapport à un ensemble correctement trié. Les propriétés ne sont pas mauvaises, mais j'ai appris à les utiliser avec parcimonie (au point d'être assez satisfait de getter et des setters; j'utilise principalement Java).
maaartinus
1

L'une des raisons pour lesquelles les propriétés ont été introduites dans Java Beans Framework était de permettre l'intégration avec un IDE - vous pouvez faire glisser ces composants vers l'IDE et modifier les propriétés à l'aide des éditeurs de propriétés.

(à l'époque, ils n'étaient que des getters et des setters réguliers - et n'étaient définis que par la convention de dénomination - et cela sentait effectivement)

Aujourd'hui, il y a encore plus de cas où les propriétés sont accessibles et modifiées non pas par le biais du code normal - comme lors du débogage.

Ophir Yoktan
la source
1

Un autre angle - ils venaient vraiment de VB. Rappelez-vous que dans les jours sombres du 20e siècle, lorsque .NET a été conçu, l'accent était mis sur le remplacement facile et rapide de VB afin que vos programmeurs VB puissent simplement glisser-déposer des éléments sur des formulaires Web. VB avait des propriétés, vous devez donc conserver cette fonctionnalité pour que les choses se traduisent correctement.

Wyatt Barnett
la source
1
Anders Heljsberg a conçu C # (et une partie de l'IL) et a également conçu Delphi - qui avait des propriétés.
Jesse C. Slicer
1

Les propriétés ont au moins deux objectifs:

  • Ils sont conceptuellement identiques aux champs même s'ils sont implémentés différemment. Un getter ne devrait pas changer l'état de l'objet et ne devrait avoir aucun effet secondaire. Un setter ne devrait changer l'état que de manière conceptuellement similaire à la modification d'un champ et ne pas avoir d'autres effets secondaires.

  • Ils sont syntaxiquement identiques aux champs publics (éventuellement en lecture seule). Cela signifie qu'un champ public peut être modifié en une propriété sans interrompre les appelants au niveau source.

dsimcha
la source
Oui, mais notez le mot "devrait". Il n'y a rien sur les propriétés qui empêche les getters d'avoir des effets secondaires et en fait j'ai vu de nombreux cas où c'était le cas.
Nemanja Trifunovic
1
Modification d' un champ public à une propriété se casser la compatibilité binaire, le code la consommation devra être recompilé - mais pas modifié que je devine est ce que vous obtenez à :)
MattDavey
@MattDavey: Merci. C'est ce que je voulais dire. Édité.
dsimcha