web-dev-qa-db-fra.com

Pourquoi un modèle de domaine anémique est-il considéré comme mauvais en C # / OOP, mais très important en F # / FP?

Dans un blog sur F # pour le plaisir et le profit, il dit:

Dans une conception fonctionnelle, il est très important de séparer le comportement des données. Les types de données sont simples et "stupides". Et puis séparément, vous disposez d'un certain nombre de fonctions qui agissent sur ces types de données.

C'est exactement l'opposé d'une conception orientée objet, où le comportement et les données sont destinés à être combinés. Après tout, c'est exactement ce qu'est une classe. En fait, dans une conception véritablement orientée objet, vous ne devriez avoir qu'un comportement - les données sont privées et ne sont accessibles que via des méthodes.

En fait, dans OOD, ne pas avoir assez de comportement autour d'un type de données est considéré comme une mauvaise chose, et a même un nom: le " modèle de domaine anémique ".

Étant donné qu'en C #, nous semblons continuer d'emprunter à F # et essayer d'écrire du code de style plus fonctionnel; comment se fait-il que nous n'empruntions pas l'idée de séparer les données/comportements et que nous ne les considérions même pas comme mauvais? Est-ce simplement que la définition ne fonctionne pas avec OOP, ou y a-t-il une raison concrète qu'elle est mauvaise en C # qui pour une raison quelconque ne s'applique pas en F # (et en fait, est inversée)?

(Remarque: je suis spécifiquement intéressé par les différences en C #/F # qui pourraient changer l'opinion de ce qui est bon/mauvais, plutôt que par des individus qui peuvent être en désaccord avec l'une ou l'autre opinion dans le billet de blog).

49
Danny Tuppeny

La principale raison FP vise cela et C # OOP ne l'est pas) est que dans FP l'accent est mis sur la transparence référentielle; c'est-à-dire que les données entrent dans une fonction et les données sortent, mais les données d'origine ne sont pas modifiées.

Dans C # OOP il y a un concept de délégation de responsabilité où vous lui déléguez la gestion d'un objet, et donc vous voulez qu'il change ses propres internes.

Dans FP vous ne voulez jamais changer les valeurs dans un objet, donc avoir vos fonctions incorporées dans votre objet n'a pas de sens.

Plus loin dans FP vous avez un polymorphisme de type supérieur permettant à vos fonctions d'être beaucoup plus généralisées que C # OOP permet. De cette façon, vous pouvez écrire une fonction qui fonctionne pour tout a, et donc l'avoir intégré dans un bloc de données n'a pas de sens; cela couplerait étroitement la méthode afin qu'elle ne fonctionne qu'avec ce type particulier de a. Un comportement comme celui-ci est très bien et commun en C # OOP parce que vous n'avez de toute façon pas la possibilité d'abstraire des fonctions de manière générale, mais en FP c'est un compromis.

Le plus gros problème que j'ai vu dans les modèles de domaine anémiques en C # OOP est que vous vous retrouvez avec du code en double parce que vous avez DTO x, et 4 fonctions différentes qui engagent l'activité f en DTO x parce que 4 différentes personnes n'ont pas vu l'autre implémentation. Lorsque vous mettez la méthode directement sur DTO x, alors ces 4 personnes voient toutes l'implémentation de f et la réutilisent.

Les modèles de données anémiques en C # OOP gênent la réutilisation du code, mais ce n'est pas le cas dans FP car une seule fonction est généralisée à tant de types différents que vous obtenir une plus grande réutilisation du code puisque cette fonction est utilisable dans beaucoup plus de scénarios qu'une fonction que vous écririez pour un seul DTO en C #.


Comme souligné dans les commentaires , l'inférence de type est l'un des avantages FP s'appuie pour permettre un polymorphisme aussi significatif, et vous pouvez en particulier remonter à Hindley Milner système de type avec inférence de type Algorithm W; une telle inférence de type dans le système de type C # OOP type a été évitée car le temps de compilation lorsque l'inférence basée sur des contraintes est ajoutée devient extrêmement long en raison de la recherche exhaustive nécessaire, détails ici: https://stackoverflow.com/questions/3968834/generics-why-cant-the-compiler-infer-the-type-arguments-in-this-case

39
Jimmy Hoffa

Pourquoi un modèle de domaine anémique est-il considéré comme mauvais en C #/OOP, mais très important en F #/FP?

Votre question a un gros problème qui limitera l'utilité des réponses que vous obtenez: vous sous-entendez/supposez que F # et FP sont similaires. FP est un vaste famille de langages, y compris la réécriture de termes symboliques, dynamique et statique. Même parmi les langages de type statique FP il existe de nombreuses technologies différentes pour exprimer des modèles de domaine tels que des modules d'ordre supérieur dans OCaml et SML (qui n'existent pas en F #). F # est l'un de ces langages fonctionnels mais il est particulièrement remarquable pour être lean et, en particulier, ne fournit ni modules d'ordre supérieur ni types de type supérieur.

En fait, je n'ai pas pu commencer à vous dire comment les modèles de domaine sont exprimés en FP. L'autre réponse ici parle très spécifiquement de la façon dont cela se fait dans Haskell et n'est pas du tout applicable à LISP (la mère de toutes les langues FP), à la famille de langues ML ou à tout autre langage fonctionnel. .

comment se fait-il que nous n'empruntions pas l'idée de séparer les données/comportements, et que nous ne les considérions même pas comme mauvais?

Les génériques peuvent être considérés comme un moyen de séparer les données et les comportements. Les génériques issus de la famille ML des langages de programmation fonctionnels ne font pas partie de la POO. C # a des génériques, bien sûr. On pourrait donc soutenir que C # emprunte lentement l'idée de séparer les données et le comportement.

Est-ce simplement que la définition ne correspond pas à la POO,

Je pense que OOP est basé sur une prémisse fondamentalement différente et, par conséquent, ne vous donne pas les outils dont vous avez besoin pour séparer les données et le comportement. À toutes fins pratiques, vous avez besoin de types de données de produit et de somme et En ML, cela signifie les types d'union et d'enregistrement et la correspondance de modèle.

Découvrez l'exemple que j'ai donné ici .

ou y a-t-il une raison concrète que c'est mauvais en C # qui pour une raison quelconque ne s'applique pas en F # (et en fait, est inversé)?

Faites attention à ne pas passer de OOP à C #. C # est loin d'être aussi puritain à propos de OOP que les autres langages. Le .NET Framework est maintenant plein de génériques, statiques) méthodes et même lambdas.

(Remarque: je suis spécifiquement intéressé par les différences en C #/F # qui pourraient changer l'opinion de ce qui est bon/mauvais, plutôt que par des individus qui peuvent être en désaccord avec l'une ou l'autre opinion dans le blog).

Le manque de types d'unions et de correspondance de modèles en C # rend presque impossible cette opération. Quand tout ce que vous avez c'est un marteau, tout ressemble à un clou ...

6
Jon Harrop