Pourquoi les classes de données sont-elles considérées comme une odeur de code?

18

Cet article prétend qu'une classe de données est une "odeur de code". La raison:

C'est une chose normale quand une classe nouvellement créée ne contient que quelques champs publics (et peut-être même une poignée de getters / setters). Mais la vraie puissance des objets est qu'ils peuvent contenir des types de comportement ou des opérations sur leurs données.

Pourquoi un objet ne contient-il que des données? Si la responsabilité principale de la classe est de représenter les données, n'ajouterait-on pas des méthodes qui opèrent sur les données en brisant le principe de responsabilité unique ?

Sipo
la source
1
Cela dépendra fortement des fonctionnalités du langage. En Python, par exemple, il n'y a pas de distinction entre le "champ" et ses accesseurs, à moins que vous ne fassiez de votre mieux pour écrire Java en Python .
jscs
1
Je pense qu'avoir certaines classes de données uniquement n'est pas une odeur de code en soi, mais si la plupart des classes sont comme ça, alors nous parlons de l'
anti
1
Je ne vois pas en quoi cette question est en double. Les autres questions concernent l'utilisation des classes de données dans OO, tandis que celle-ci concerne les inconvénients des classes de données - des sujets entièrement différents.
Milos Mrdovic
Vous voudrez peut-être lire cette réponse sur stackoverflow qui est beaucoup plus différenciée que la réponse la plus votée ici qui postule l'infériorité du modèle de domaine riche et la présente comme un fait prouvé. stackoverflow.com/questions/23314330/…
McLovin

Réponses:

31

Il n'y a absolument rien de mal à avoir des objets de données purs. Franchement, l'auteur de la pièce ne sait pas de quoi il parle.

Une telle pensée découle d'une vieille idée, échouée, que "true OO" est la meilleure façon de programmer et que "true OO" est tout au sujet de "modèles de données riches" où l'on mélange les données et les fonctionnalités.

La réalité nous a montré qu'en réalité l'inverse est vrai, en particulier dans ce monde de solutions multi-thread. Les fonctions pures, combinées à des objets de données immuables, constituent un moyen manifestement meilleur de coder.

David Arno
la source
1
Je voulais juste ajouter que les objets de données purs peuvent être inestimables pour la modélisation des relations, la validation, le contrôle des accès / modifications.
Adrian
2
Bien que ce soit bien si ces fonctions pures qui ne prennent qu'une instance de l'objet de données immuable comme argument sont implémentées en tant que méthodes sur l'objet de données.
RemcoGerlich
7
Si la fonction prend un argument de ce type de données, il est déjà couplé aux données.
RemcoGerlich
4
Vote en aval parce que c'est incorrect, ou au mieux une question d'opinion. Dans un langage OO, il est généralement logique qu'un objet contienne à la fois les données (qui peuvent toujours être immuables!) Et les méthodes qui agissent dessus. Les fonctions pures et les données séparées sont excellentes dans d'autres paradigmes de langage, mais si vous faites OO, faites OO complètement.
Marnen Laibow-Koser
3
La réalité nous a montré beaucoup de choses. -1 pour tout savoir dogmatique "les autres ont échoué". De plus, l'auteur ne dit pas que les objets de données purs sont "faux", mais simplement qu'ils sont une "odeur de code" et dignes d'être questionnés. Je regrette seulement d'avoir un downvote à donner pour mon pays. :-)
user949300
7

Il n'y a absolument rien de mal à avoir des objets de données purs. L'auteur a une opinion non partagée par les développeurs de logiciels que je connais.

Surtout pour le mappage de bases de données, vous avez en général des classes d'entités qui ne contiennent que les champs stockés dans la base de données et les getters et setters. Wikipedia Hibernate (framework)

L'idée de trou des beans Java utilisée par de nombreux outils / frameworks est basée sur des classes de données appelées beans qui ne contiennent que des champs et les getters et setters associés. Wikipdia JavaBeans

Fazit:
Si quelqu'un prétend que quelque chose est «mauvais» ou «une odeur de code», vous devriez toujours chercher les raisons données. Si les raisons ne vous convaincent pas, demandez à quelqu'un d'autre de meilleures raisons ou une opinion différente. (Comme vous l'avez fait dans ce forum)

MrSmith42
la source
L'auteur ne dit pas que les objets de données purs sont "faux". Ils disent que les objets de données purs sont une "odeur de code", ce qui signifie que vous devriez réfléchir à deux fois avant de les utiliser.
user949300
1
@ user949300, vous semblez confus. Si l'auteur se réfère à eux comme une odeur de code, alors il indique qu'il pourrait y avoir quelque chose de mal avec eux. Comme ils sont largement reconnus ces jours-ci comme une très bonne pratique, ils ne sont clairement pas une odeur de code. Ainsi, MrSmith42 a raison: il n'y a absolument rien de mal avec eux.
David Arno
4

Un bon argument pourquoi par Martin Fowler:

"Tell-Don't-Ask est un principe qui aide les gens à se rappeler que l'orientation objet consiste à regrouper des données avec les fonctions qui opèrent sur ces données. Cela nous rappelle qu'au lieu de demander des données à un objet et d'agir sur ces données, nous devrait plutôt indiquer à un objet ce qu'il doit faire. Cela encourage à déplacer le comportement dans un objet pour aller avec les données. "

https://martinfowler.com/bliki/TellDontAsk.html

Curtis Yallop
la source
1
Le problème ici est que Fowler restreint artificiellement «ne demande pas» en changeant les fonctions de demander une portée plus large à simplement demander la portée de l'objet. Ils demandent toujours. «Dites, ne demandez pas» peut en fait aller plus loin en disant vraiment ces fonctions via leurs listes d'arguments. Et nous arrivons ainsi à des objets de données et à des fonctions distinctes (au niveau des données) constituant la véritable implémentation de «ne demandez pas». Donc, plutôt que d'être un bon argument pour affirmer que les classes de données sont une odeur de code, cela prouve en fait le contraire.
David Arno
2
@DavidArno Vous oubliez l'encapsulation et la dissimulation. Vous dites à un objet d'exécuter une méthode membre, et la méthode membre va dans la boîte noire de l'objet et fait tout ce qu'elle doit pour obtenir la réponse. Si vous demandez à un objet de l'extérieur, vous n'avez pas accès à son état privé, et donc soit l'objet expose plus d'état qu'il n'est sage, soit le demandeur doit sauter à travers plus de cerceaux que nécessaire. Je ne vois pas pourquoi vous auriez jamais «demandé» un objet dans un environnement OO. (D'autres paradigmes de programmation peuvent nécessiter des approches différentes, bien sûr.)
Marnen Laibow-Koser
2
@DavidArno Bien sûr, vous pouvez transmettre bazun paramètre à une méthode statique, mais pour ce faire, vous devez d'abord demander à l'objet de le faire. Peut-être que dans un paradigme de programmation où les méthodes étaient primaires (comme, par exemple, la programmation fonctionnelle), cela a du sens, mais dans un environnement OO, ce n'est absolument pas le cas, car les objets sont primaires et doivent contenir à la fois les données et les fonctions pour agir en conséquence. Votre affirmation selon laquelle la suppression de la méthode de l'objet a augmenté l'encapsulation est également exactement à l'envers , pour autant que je sache, car cela signifie que vous avez maintenant une bazapparence en dehors de l'objet.
Marnen Laibow-Koser
1
@ MarnenLaibow-Koser, je ne prétends pas "faire OO". J'écris du code et j'utilise de bonnes techniques pour le faire. Que ces techniques proviennent du paradigme fonctionnel, ou du paradigme OO, ou du paradigme qui donne un putain de foutre, cela ne m'intéresse pas. Choisir un paradigme et s'y tenir le plus complètement possible est un pur dogme. C'est mauvais. C'est ridicule. Il vous étouffe et entraîne un code inférieur. Ne le fais pas.
David Arno
1
@DavidArno Au contraire, si vous vous engagez pleinement dans un paradigme (tout paradigme décent, pas seulement OO), vous obtenez de puissantes abstractions de haut niveau et du code logiquement cohérent et maintenable. Je ne dis pas que cela est dogmatique, mais plutôt pragmatique. J'ai vu et conservé trop de code qui a apparemment été produit avec une attitude comme la vôtre, où l'auteur ne s'est pas vraiment engagé à la cohérence logique du système utilisé. C'est difficile à comprendre, difficile à maintenir et difficile à modifier. Aucun paradigme n'est parfait, mais généralement un mélange (à moins d'être soigneusement étudié) est plus difficile à comprendre.
Marnen Laibow-Koser
2

Ce que vous devez comprendre, c'est qu'il existe deux types d'objets:

  • Objets qui ont un comportement . Ceux-ci devraient s'abstenir de donner au public l'accès à la plupart des membres de leurs données. Je n'attends que très peu de méthodes d'accesseur définies pour celles-ci.

    Un exemple serait une expression régulière compilée: l'objet est créé pour fournir un certain comportement (pour faire correspondre une chaîne à une expression rationnelle spécifique et pour signaler les correspondances (partielles)), mais la façon dont l'expression régulière compilée fait son travail n'est pas celle de l'utilisateur. affaires.

    La plupart des classes que j'écris appartiennent à cette catégorie.

  • Des objets qui ne sont vraiment que des données . Ceux-ci devraient simplement déclarer tous leurs membres publics (ou fournir l'ensemble complet d'accesseurs pour eux).

    Un exemple serait une classe Point2D. Il n'y a absolument aucun invariant qui doit être assuré pour les membres de cette classe, et les utilisateurs devraient pouvoir simplement accéder aux données via myPoint.xet myPoint.y.

    Personnellement, je n'utilise pas beaucoup ces classes, mais je suppose qu'il n'y a pas de plus gros morceau de code que j'ai écrit qui n'utilise pas une telle classe quelque part.

Pour devenir compétent en orientation d'objet, il faut comprendre que cette distinction existe et apprendre à classer la fonction d'une classe dans l'une de ces deux catégories.


Si vous codez en C ++, vous pouvez rendre cette distinction explicite en utilisant classpour la première catégorie d'objets et structpour la seconde. Bien sûr, les deux sont équivalents, sauf que cela classsignifie que tous les membres sont privés par défaut, alors que structtous les membres sont publics par défaut. C'est exactement le genre d'informations que vous souhaitez communiquer.

cmaster - réintégrer monica
la source
1
Une explication pour le downvote serait cool ...
cmaster - reinstate monica
Merci d'avoir répondu. Je déteste quand les gens votent sans raison. Si vous avez un problème avec une réponse, expliquez pourquoi .
Sipo