Au cours des dernières années, les langues que j'aime utiliser sont de plus en plus «fonctionnelles». J'utilise maintenant des langages qui sont une sorte d '"hybride": C #, F #, Scala. J'aime concevoir mon application en utilisant des classes qui correspondent aux objets du domaine et utiliser des fonctionnalités fonctionnelles où cela rend le codage plus facile, plus coïncident et plus sûr (en particulier lors de l'utilisation de collections ou lors du passage de fonctions).
Cependant, les deux mondes se "heurtent" quand il s'agit de concevoir des modèles. L'exemple spécifique que j'ai rencontré récemment est le modèle Observer. Je veux qu'un producteur notifie un autre code (les "consommateurs / observateurs", par exemple un stockage de base de données, un enregistreur, etc.) lorsqu'un élément est créé ou modifié.
Au départ, je l'ai fait "fonctionnellement" comme ceci:
producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed
Mais je me demande maintenant si je devrais utiliser une approche plus "OO":
interface IItemObserver {
onNotify(Item)
}
class DBObserver : IItemObserver ...
class LogObserver: IItemObserver ...
producer.addObserver(new DBObserver)
producer.addObserver(new LogObserver)
producer.foo() //calls observer in a loop
Quels sont les avantages et les inconvénients des deux approches? J'ai entendu un jour un gourou de la FP dire que les modèles de conception n'étaient là qu'en raison des limites du langage, et c'est pourquoi il y en a si peu dans les langages fonctionnels. Peut-être que cela pourrait en être un exemple?
EDIT: Dans mon scénario particulier, je n'en ai pas besoin, mais .. comment implémenteriez-vous la suppression et l'ajout "d'observateurs" de manière fonctionnelle? (C'est-à-dire comment implémenteriez-vous toutes les fonctionnalités du modèle?) Juste passer une nouvelle fonction, par exemple?
la source
Réponses:
C'est un bon exemple de deux approches différentes qui portent la notion d'effectuer une tâche en dehors de la limite de préoccupation pour l'objet appelant.
Bien qu'il soit clair dans cet exemple que vous devez opter pour l'approche fonctionnelle, en général, cela dépendra vraiment de la complexité du comportement de l'objet appelé. Si c'est vraiment une question de comportement complexe, où vous vous retrouverez à réappliquer souvent une logique similaire et où les générateurs de fonctions ne peuvent pas être utilisés pour l'exprimer clairement, alors vous voudrez probablement opter pour la composition ou l'héritage de classe, où vous aurez avoir un peu plus de liberté pour réutiliser et étendre le comportement existant sur une base ad hoc.
Un modèle que j'ai observé, cependant, est que généralement les développeurs optent pour l'approche fonctionnelle au départ et seulement une fois que la demande pour un comportement plus granulaire apparaît, ils décident d'opter pour une approche basée sur les classes. Je sais, par exemple, que Django est passé des vues basées sur les fonctions, des chargeurs de modèles et des exécuteurs de test à celles basées sur les classes une fois que les avantages et les exigences sont devenus évidents, mais pas avant.
la source
La version fonctionnelle est beaucoup plus courte, plus facile à entretenir, plus facile à lire et, en général, largement supérieure à presque tous les égards imaginables.
Beaucoup, bien que loin de tout, les modèles doivent compenser le manque de fonctionnalités dans la POO, comme les observateurs. Celles-ci sont bien mieux modélisées fonctionnellement.
la source
Votre «gourou FP» a partiellement raison; de nombreux modèles OO sont des hacks pour faire des choses fonctionnelles. (Son affirmation selon laquelle c'est la raison pour laquelle il y a peu de langages de PF semble au mieux douteuse.) Les modèles Observer et Strategy essaient d'imiter des fonctions de première classe. Le modèle de visiteur est un hack pour simuler la correspondance des modèles. Votre
IItemObserver
est juste une fonction déguisée. Prétendre que c'est différent de toute autre fonction qui prend un article ne vous rapporte rien.Les objets ne sont qu'un type d'abstraction des données. Ce document aidera à faire la lumière sur ce sujet. Les objets peuvent être utiles, mais il est important de reconnaître qu'ils ne conviennent pas à tout. Il n'y a pas de dichotomie; il s'agit simplement de choisir le bon outil pour le bon travail, et la programmation fonctionnelle ne nécessite en aucun cas que vous abandonniez des objets. Cela mis à part, la programmation fonctionnelle est plus qu'une simple utilisation de fonctions. Il s'agit également de minimiser les effets secondaires et les mutations.
la source
Je ne peux pas vraiment répondre à la question parce que je ne suis pas bon en langages fonctionnels; mais je pense que vous devriez être moins préoccupé par l'approche tant que ce que vous avez fonctionne. D'après ce que je comprends, tant que vous n'ajoutez plus d'écouteurs à l'avenir ou que vous ne changez pas d'écouteurs pendant l'exécution, vous pouvez très bien ignorer le modèle Observer ici.
Je ne suis pas d'accord que les modèles de conception compensent les "limitations" dans les langues OO. Ils sont là pour faire bon usage des fonctionnalités de POO. Le polymorphisme et l'héritage sont des caractéristiques et non des limitations. Les modèles de conception utilisent ces fonctionnalités pour promouvoir une conception flexible. Vous pouvez créer un programme absolument non OO dans un OO. Vous pouvez, avec beaucoup de soin, écrire un programme complet contenant des objets sans état, imitant FP.
la source