Dans un article de blog sur F # pour le plaisir et le profit, il est écrit:
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 avez un certain nombre de fonctions qui agissent sur ces types de données.
C'est l'opposé exact d'une conception orientée objet, où comportement et données doivent être combinés. Après tout, c'est exactement ce que sont les cours. En fait, dans une conception véritablement orientée objet, vous ne devez avoir que du comportement: les données sont privées et ne peuvent être consultées 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 à emprunter à F # et à essayer d'écrire un code de style plus fonctionnel; comment se fait-il que nous n'empruntions pas l'idée de séparer les données / le comportement, et même de le considérer comme mauvais? Est-ce simplement que la définition ne correspond pas à la POO, ou y a-t-il une raison concrète pour laquelle c'est mauvais en C # qui, pour une raison quelconque, ne s'applique pas en F # (et en fait, est inversée)?
(Remarque: je suis particulièrement intéressé par les différences de C # / F # qui pourraient changer l'opinion de ce qui est bon / mauvais, plutôt que les individus qui peuvent être en désaccord avec l'une ou l'autre de ces opinions dans l'article du blog).
la source
Réponses:
La raison principale pour laquelle FP cherche à atteindre cet objectif, contrairement à C # OOP, c’est que dans ce dernier, l’accent est mis sur la transparence référentielle; c'est-à-dire que les données vont dans une fonction et que les données sortent, mais les données d'origine ne sont pas modifiées.
Dans C # OOP, il existe un concept de délégation de responsabilité selon lequel vous déléguez la gestion d'un objet à celui-ci et vous souhaitez donc que celui-ci modifie ses propres internes.
Dans FP, vous ne voulez jamais changer les valeurs d'un objet. Par conséquent, l'intégration de vos fonctions dans votre objet n'a pas de sens.
Plus loin dans FP, vous avez un polymorphisme plus élevé, ce qui permet à vos fonctions d’être beaucoup plus généralisées que COP ne le permet. De cette façon, vous pouvez écrire une fonction qui fonctionne pour tout
a
, et donc l’incorporer dans un bloc de données n’a aucun sens; cela couplerait étroitement la méthode pour qu’elle ne fonctionne que avec ce type particulier dea
. Un tel comportement est très courant dans C # OOP car vous n’avez pas la capacité d’abstraire des fonctions aussi généralement de toute façon, mais en PF, c’est un compromis.Le plus gros problème que j'ai rencontré dans les modèles de domaine anémique dans C # OOP est que vous vous retrouvez avec du code en double car vous avez DTO x et 4 fonctions différentes qui engagent l'activité f dans DTO x car 4 personnes différentes n'ont pas vu l'autre implémentation. . Lorsque vous mettez la méthode directement sur DTO x, ces 4 personnes voient toutes la mise en oeuvre de f et la réutilisent.
Les modèles de données anémiques dans C # OOP empêchent la réutilisation du code, mais ce n'est pas le cas dans FP car une seule fonction est généralisée à travers autant de types différents que vous obtenez une plus grande réutilisation du code car cette fonction est utilisable dans beaucoup plus de scénarios qu'une fonction que vous utilisez. écrirait pour un seul DTO en C #.
Comme indiqué dans les commentaires , l'inférence de type est l'un des avantages sur lesquels s'appuie FP pour permettre un polymorphisme aussi important, et vous pouvez en particulier le retrouver dans le système de type Hindley Milner avec l'inférence de type de l'algorithme W; une telle inférence de type dans le système de types C # OOP a été évitée car le temps de compilation lorsque l'ajout d'une inférence basée sur des contraintes devient extrêmement long en raison de la recherche exhaustive nécessaire, voir les détails ici: https://stackoverflow.com/questions/3968834/generics-why -cant-the-compiler-infer-the-type-arguments-in-this-case
la source
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. La PF est une immense famille de langues comprenant la réécriture de termes symboliques, dynamique et statique. Même parmi les langages de FP statiques, il existe de nombreuses technologies différentes pour exprimer des modèles de domaine, tels que les 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 sa maigreur et, en particulier, ne fournit pas de modules d'ordre supérieur ni de types de type supérieur.
En fait, je ne pourrais pas commencer à vous dire comment les modèles de domaine sont exprimés dans FP. L’autre réponse ici parle très précisément de la façon dont cela se fait en Haskell et ne s’applique pas du tout à Lisp (la mère de toutes les langues de la PF), à la famille de langues ML ou à toute autre langue fonctionnelle.
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 de la famille ML de langages de programmation fonctionnels ne font pas partie de la programmation orientée objet. C # a des génériques, bien sûr. On pourrait donc soutenir que C # emprunte lentement l’idée de la séparation des données et du comportement.
Je pense que la programmation orientée objet repose sur une prémisse fondamentalement différente et, par conséquent, ne vous donne pas les outils nécessaires pour séparer les données et le comportement. À toutes fins utiles, vous avez besoin des types de données produit et somme et de leur envoi. En ML, cela signifie des types d'union et d'enregistrement et une correspondance de modèle.
Découvrez l'exemple que j'ai donné ici .
Faites attention à ne pas sauter de la POO au C #. C # est loin d’être aussi puritain en matière de POO que d’autres langues. Le .NET Framework regorge maintenant de génériques, de méthodes statiques et même de lambdas.
L'absence de types d'union et de correspondance de modèle en C # rend la tâche presque impossible. Quand tout ce que vous avez est un marteau, tout ressemble à un clou ...
la source
Je pense que dans une application métier, vous ne voulez souvent pas cacher les données, car la correspondance de modèle sur des valeurs immuables est très utile pour garantir que vous couvrez tous les cas possibles. Mais si vous implémentez des algorithmes complexes ou des structures de données, il vaut mieux cacher les détails de cette implémentation en transformant les ADT (types de données algébriques) en ADT (types de données abstraits).
la source