Design en langages «mixtes»: design orienté objet ou programmation fonctionnelle?

11

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?

Lorenzo Dematté
la source
Et les acteurs?
kiritsuku
Oubliez les cours et tous ces trucs inutiles OOPish. Vous feriez mieux de penser en termes de modules à la place (voir les langages SML et OCaml pour une inspiration).
SK-logic
@Antoras Si vous pouviez faire une comparaison avec une approche basée sur les acteurs, ce serait plus que bienvenue :)
Lorenzo Dematté
2
@ dema80, OCaml est parfaitement multi-paradigme. Les modules ne sont pas du tout liés à la programmation fonctionnelle. Il existe par exemple un système de modules avancé dans un Ada purement impératif. Et toute la notoriété acquise par OOP devrait en fait aller aux modules - tout ce qui est bon dans OOP n'est que diverses formes de simulation de la fonctionnalité des modules. Vous pouvez oublier totalement toutes ces classes et utiliser leur syntaxe pour exprimer des modules à la place, en pensant en termes de modules, pas de POO. Btw., C'est exactement ce que Microsoft a fait avec son mscorlib - pas beaucoup de POO là-bas, juste des modules et des espaces de noms.
SK-logic
1
Je pense que la meilleure question est "Y a-t-il une clarté ou une organisation que vous perdez en le faisant de la manière de la PF?"
djechlin

Réponses:

0

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.

Filip Dupanović
la source
Je pense que cette réponse est un peu erronée. La programmation fonctionnelle n'est pas la programmation (uniquement avec) des fonctions. La programmation fonctionnelle utilise également des abstractions, il n'y a donc pas de dichotomie ici.
Doval
"composition ou héritage, où vous aurez un peu plus de liberté pour réutiliser et étendre le comportement existant" vous parlez comme ceci N'EST PAS l'un des principaux avantages du FP pur. la modularité, la composabilité et la réutilisabilité se produisent naturellement lorsque vous codez de manière fonctionnelle, ce n'est pas une "chose OOP"
sara
@ FilipDupanović Je faisais référence à la réutilisation et à l'extension du code existant. les fonctions pures sont probablement les choses les plus composables de toute la programmation. vous êtes écrit comme si vous ne pouviez pas gérer la complexité dans un environnement fonctionnel pur. la gestion de la complexité en composant des pièces simples en parties plus grandes mais toujours simples et opaques est le cœur même de la FP, et beaucoup diraient que c'est encore mieux que la POO. Je ne suis pas d'accord avec la dichotomie entre "des lignes de fonction fonctionnelles qui n'évoluent pas VS OOP solide qui n'évolue pas de problème" que vous proposez.
sara
vous avez dit que FP était inférieur en ce qui concerne la composition et la réutilisation de code, et que lorsque les applications deviennent plus complexes, la POO sera plus ou moins toujours "meilleure". Je prétends que c'est tout simplement faux et trompeur, et j'ai donc pensé que cela justifiait un downvote. Est-ce vraiment du "fanatisme puriste"? Je ne discuterai pas de cela plus loin ici, car les commentaires ne sont pas destinés à de longues discussions comme celles-ci.
sara
5

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.

DeadMG
la source
Je suis d'accord et j'ai le même sentiment.
Lorenzo Dematté
Je suis d'accord et j'ai le même sentiment.Mais j'étais en quelque sorte en train de "tricher" avec mon code fonctionnel: dans mon cas, c'est tout ce dont j'ai besoin, mais si j'ai besoin d'ajouter et de supprimer des "observateurs"? J'ai édité ma question
Lorenzo Dematté
5

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 IItemObserverest 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.

Doval
la source
+1 pour (IMO) une bonne réponse. Merci aussi pour le lien vers le journal: je l'avais lu il y a quelque temps et je l'avais perdu. Maintenant je l'ai retrouvé.
Giorgio
-1

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.

DPD
la source
1
"Vous pouvez, avec beaucoup de soin, écrire un programme entier contenant des objets qui n'ont pas d'état, imitant la PF.", Bien sûr, et grâce à la discipline, vous pouvez faire de même dans les langages impératifs. :) En général, je ne pense pas que les patrons de conception compensent les limitations, mais considérons le cas du modèle Visitor.
Lorenzo Dematté
De plus, je ne suis pas concerné par le code: cependant, je voudrais confronter mes collègues programmeurs sur l'approche (à ma connaissance, c'est à cela que sert programmers.se, sinon j'aurais posté mon Q sur SO)
Lorenzo Dematté
Chaque fois que vous repérez un motif répétitif, ce n'est rien d'autre qu'une indication d'une grave limitation de votre langage et de votre cadre de modélisation. Il n'y aura aucun modèle du tout dans un monde idéal.
SK-logic
@ SK-logic: Malheureusement, le monde n'est pas idéal et le monde réel a des modèles. Quelle est la raison pour laquelle les langages OO ont l' héritage, à mettre en œuvre le code repeateable sans avoir à les réécrire sur tout objet du monde again.Real ont un état, c'est pourquoi il y a un modèle d'État
DPD
1
@ SK-logic: Je suis bien conscient que certains langages sont programmables, et certains permettent également d'appliquer les effets du système de type. Mon point est que, comme "OOP", "motif" est un terme malheureusement mal compris. Un modèle est quelque chose qui réapparaît sous des formes similaires mais différentes , qui n'admet pas une solution uniforme . Parfois, vous pouvez faire disparaître ces quasi-répétitions - les méthodes multiples rendent redondant les visiteurs / doubles. Cela ne signifie pas que "tous les schémas sont des signes de carence", car cela signifie que le monde réel est déficient. Les modèles proviennent de l' architecture , pas du logiciel.
Frank Shearar