Programmation événementielle: quand en vaut-il la peine?

19

Ok, je sais que le titre de cette question est presque identique à Quand dois-je utiliser la programmation basée sur les événements? mais les réponses à cette question ne m'ont pas aidé à décider si je devrais utiliser les événements dans le cas particulier auquel je suis confronté.

Je développe une petite application. C'est une application simple, et pour la plupart, sa fonctionnalité est CRUD de base.

Lors de certains événements (lors de la modification de certaines données), l'application doit écrire une copie locale desdites données dans un fichier. Je ne sais pas quelle est la meilleure façon de mettre en œuvre cela. Je peux:

  • Déclenche des événements lorsque les données sont modifiées et lie une réponse (générer le fichier) à ces événements. Vous pouvez également implémenter le modèle d'observateur. Cela semble être une complexité inutile.
  • Appelez le code générateur de fichier directement à partir du code qui modifie les données. Beaucoup plus simple, mais il semble faux que la dépendance soit ainsi, c'est-à-dire qu'il semble faux que la fonctionnalité de base de l'application (code qui modifie les données) soit couplée à cet avantage supplémentaire (code qui génère un fichier de sauvegarde). Je sais cependant que cette application n'évolue pas à un point où ce couplage pose problème.

Quelle est la meilleure approche dans ce cas?

abl
la source
2
Je dirais de ne rien implémenter vous-même - utilisez simplement un bus d'événements existant. Cela rendra la vie beaucoup plus simple ...
Boris the Spider
La programmation événementielle est intrinsèquement asynchrone. Les événements peuvent ou non se produire, dans l'ordre que vous vouliez ou peut-être dans un autre ordre ou pas du tout. Si vous pouvez faire face à cette complexité supplémentaire, allez-y.
Pieter B
L'événementiel signifie généralement que votre code est fourni sous forme de rappels et que ceux-ci sont invoqués ailleurs d'une manière que vous ne pouvez pas prévoir. Votre description ressemble davantage à cela lorsque quelque chose de spécifique se produit dans votre code, vous devez faire plus qu'une implémentation naïve. Il suffit de coder les appels supplémentaires.
Thorbjørn Ravn Andersen
Il existe une différence entre les événements et les événements. Voir par exemple l' épisode 355 du podcast .NET Rocks, très stimulant avec Ted Faison, Ted Faison prend les événements à la limite! ( URL de téléchargement direct ) et le livre Programmation événementielle: porter les événements à la limite .
Peter Mortensen
L'entretien avec Ted Faison commence à 13 min 10 secondes.
Peter Mortensen

Réponses:

31

Suivez le principe KISS: Keep It Simple, Stupid, ou le principe YAGNI: vous n'allez pas en avoir besoin.

Vous pouvez écrire le code comme:

void updateSpecialData() {
    // do the update.
    backupData();
}

Ou vous pouvez écrire du code comme:

void updateSpecialData() {
     // do the update.
     emit SpecialDataUpdated();
}

void SpecialDataUpdatedHandler() {
     backupData();
}

void configureEventHandlers() {
     connect(SpecialDataUpdate, SpecialDataUpdatedHandler);
}

En l'absence d'une raison impérieuse de faire autrement, suivez la voie la plus simple. Des techniques telles que la gestion des événements sont puissantes, mais elles augmentent la complexité de votre code. Cela nécessite plus de code pour fonctionner, et il est plus difficile de suivre ce qui se passe dans votre code.

Les événements sont très critiques dans la bonne situation (imaginez essayer de faire de la programmation d'interface utilisateur sans événements!) Mais ne les utilisez pas lorsque vous pouvez KISS ou YAGNI à la place.

Winston Ewert
la source
J'aime particulièrement le fait que vous ayez mentionné que le déclenchement d'événements lorsque les données sont modifiées n'est pas anodin.
NoChance
13

L'exemple que vous décrivez d'une donnée simple, où la modification déclenche un certain effet, peut parfaitement être implémenté avec le modèle de conception d'observateur :

  • c'est plus simple à mettre en œuvre et à maintenir que le code complet basé sur les événements.
  • le couplage entre sujet et observateur peut être abstrait, ce qui facilite la séparation des préoccupations.
  • il est idéal pour une relation un à plusieurs (les sujets ont un ou plusieurs observateurs).

L'approche événementielle vaut son investissement pour des scénarios plus complexes, lorsque de nombreuses interactions différentes peuvent se produire, dans un contexte plusieurs-à-plusieurs, ou si des réactions en chaîne sont envisagées (par exemple, un sujet informe un observateur, qui dans certains cas souhaite modifier la sujet ou autres sujets)

Christophe
la source
1
Je suis confus, l'observateur n'est-il pas une seule façon de mettre en œuvre des événements?
svick
1
@svick Je ne pense pas. Dans la programmation événementielle, vous disposez d'une boucle principale qui traite les événements dans une relation plusieurs à plusieurs, avec des émetteurs et des observateurs découplés. Je pense que l'observateur peut contribuer en traitant un type d'événement perticulaire, mais vous ne pouvez pas atteindre le spectre complet d'EDP uniquement avec un observateur. Je pense que la confusion vient du fait que dans les logiciels pilotés par les événements, les observateurs sont parfois implémentés en plus du traitement des événements (généralement MVC avec une interface graphique)
Christophe
5

Comme vous le dites, les événements sont un excellent outil pour réduire le couplage entre les classes; Ainsi, même si cela peut impliquer l'écriture de code supplémentaire dans certains langages sans prise en charge intégrée des événements, cela réduit la complexité de la vue d'ensemble.

Les événements sont sans doute l'un des outils les plus importants d'OO (selon Alan Kay - les objets communiquent en envoyant et en recevant des messages ). Si vous utilisez un langage qui prend en charge les événements ou traite les fonctions comme des citoyens de première classe, leur utilisation est une évidence.

Même dans les langues sans support intégré, la quantité de passe-partout pour quelque chose comme le modèle Observer est assez minime. Vous pourriez être en mesure de trouver une bibliothèque d'événements générique décente quelque part que vous pouvez utiliser dans toutes vos applications pour minimiser le passe-partout. (Un agrégateur d'événements générique ou un médiateur d'événements est utile dans presque tous les types d'applications).

Est-ce utile dans une petite application? Je dirais certainement oui .

  • Garder les classes découplées les unes des autres maintient votre graphique de dépendance de classe propre.
  • Les classes sans dépendances concrètes peuvent être testées isolément sans tenir compte des autres classes dans les tests.
  • Les classes sans dépendances concrètes nécessitent moins de tests unitaires pour une couverture complète.

Si vous pensez "Oh mais ce n'est vraiment qu'une très petite application, cela n'a pas vraiment d'importance" , considérez:

  • Les petites applications finissent parfois par être combinées avec des applications plus grandes par la suite.
  • Les petites applications sont susceptibles d'inclure au moins une partie de la logique ou des composants qui devront éventuellement être réutilisés dans d'autres applications.
  • Les exigences pour les petites applications peuvent changer, ce qui nécessite de refactoriser, ce qui est plus facile lorsque le code existant est découplé.
  • Des fonctionnalités supplémentaires peuvent être ajoutées ultérieurement, ce qui nécessite d'étendre le code existant, ce qui est également beaucoup plus facile lorsque le code existant est déjà découplé.
  • Le code à couplage lâche ne prend généralement pas beaucoup plus de temps à écrire qu'un code à couplage serré; mais le code étroitement couplé prend beaucoup plus de temps pour refactoriser et tester que le code faiblement couplé.

Dans l'ensemble, la taille d'une application ne devrait pas être un facteur décisif pour savoir si les classes doivent être couplées de manière lâche; Les principes SOLID ne sont pas seulement pour les grandes applications, ils s'appliquent aux logiciels et aux bases de code à n'importe quelle échelle.

En fait, le temps économisé en tests unitaires de vos classes faiblement couplées isolément devrait contrebalancer tout temps supplémentaire passé à découpler ces classes.

Ben Cottrell
la source
2

Le modèle d'observateur peut être implémenté d'une manière beaucoup plus petite que l'article Wikipedia (ou le livre GOF) le décrit, en supposant que vos langages de programmation prennent en charge quelque chose comme des "rappels" ou des "délégués". Passez simplement une méthode de rappel dans votre code CRUD (la méthode Observer, qui peut être soit une méthode générique "d'écriture dans un fichier", soit une méthode vide). Au lieu de "déclencher un événement", appelez simplement ce rappel.

Le code résultant ne sera que légèrement plus complexe que d'appeler directement le code générateur de fichiers, mais sans les inconvénients d'un couplage étroit de composants non liés.

Cela vous apportera "le meilleur des deux mondes", sans sacrifier le découplage pour "YAGNI".

Doc Brown
la source
Cela fonctionne la première fois Doc, mais lorsque l'événement doit déclencher une deuxième chose, il est probable que la classe devra être modifiée de cette façon. Si vous utilisez un modèle d'observateur, vous pouvez ajouter autant de nouveaux comportements que vous le souhaitez sans ouvrir la classe d'origine pour la modifier.
RubberDuck
2
@RubberDuck: le PO recherchait une solution "en évitant une complexité inutile" - si différents événements / comportements différents étaient nécessaires, il ne considérerait probablement pas le modèle d'observateur comme trop complexe. Je suis donc d'accord avec ce que vous avez dit, lorsque les choses deviennent plus complexes, un observateur complet le servirait mieux, mais seulement alors.
Doc Brown
Une déclaration juste, mais cela me semble être une fenêtre cassée.
RubberDuck
2
@RubberDuck: ajouter un observateur complet, avec la mécanique éditeur / abonné "au cas où", quand ce n'est pas vraiment nécessaire, me semble comme une ingénierie excessive - ce n'est pas mieux.
Doc Brown
1
Je ne suis pas en désaccord avec le fait que cela peut être sur l'ingénierie. Je ressens probablement ce que je fais parce que c'est trivial à implémenter dans ma pile de choix. Quoi qu'il en soit, nous accepterons simplement d'être en désaccord?
RubberDuck