Dans ce article 2003 de Stephen Figgins sur linuxdevcenter.com , le BitTorrent de Bram Cohen est décrit comme utilisant le modèle de conception "Fix Everything".
Une approche moins courante qui rend BitTorrent plus difficile à saisir, mais mérite d'être étudiée, est l'utilisation par Cohen de l'idempotence. Un processus est idempotent lorsque l'appliquer plus d'une fois ne provoque aucun autre changement. Cohen dit qu'il utilise un modèle de conception qu'il appelle "Tout réparer", une fonction qui peut réagir à un certain nombre de changements sans vraiment noter ce que tout cela pourrait changer. Il explique: "vous notez l'événement qui s'est produit, puis appelez la fonction de correction de tout qui est écrite de cette manière très idempotente, et nettoie tout ce qui pourrait se passer et recalcule le tout à partir de zéro." Bien que l'idempotence facilite certains calculs difficiles, elle rend les choses un peu compliquées. Il n'est pas toujours clair ce qu'un appel va changer, le cas échéant. Vous n'avez pas besoin de savoir à l'avance. Vous êtes libre d'appeler la fonction, juste pour être du bon côté.
Cela semble assez agréable à première vue.
Cependant, il me semble que le fait d'appeler une fonction idempotente de "tout réparer" améliorerait la robustesse du système au détriment de l'efficacité et pourrait bousiller le système conteneur (qui pourrait préférer les processus qui planifient et s'exécutent soigneusement).
Je ne peux pas dire que je l'ai déjà utilisé. Je ne peux pas non plus trouver la source de son application en ligne (mais j'ai trouvé celui-ci qui prétend être basé sur cela.). Je ne peux pas non plus y trouver de référence en dehors de cet article (et je considère que mon google-fu est assez bon) mais j'ai trouvé une entrée pour "Idempotent Capability" sur SOApatterns.org .
Cette idée est-elle mieux connue sous un autre nom?
Qu'est-ce que le modèle de conception "Tout réparer"? Quels sont ses avantages et ses inconvénients?
Disons que vous avez une page HTML qui est assez compliquée - si vous choisissez quelque chose dans une liste déroulante, un autre contrôle peut apparaître ou les valeurs dans un troisième contrôle peuvent changer. Vous pouvez aborder cela de deux manières:
Écrivez un gestionnaire distinct, pour chaque contrôle, qui répond aux événements sur ce contrôle et met à jour les autres contrôles si nécessaire.
Écrivez un gestionnaire unique qui examine l'état de tous les contrôles de la page et juste corrige tout .
Le deuxième appel est "idempotent" car vous pouvez l'appeler encore et encore et les commandes seront toujours correctement organisées. Alors que le ou les premiers appels peuvent avoir des problèmes si un appel est perdu ou répété, par ex. si l'un des gestionnaires effectue une bascule.
La logique du deuxième appel serait un peu plus obscure, mais vous n'avez qu'à écrire un gestionnaire.
Et vous pouvez toujours utiliser les deux solutions, appelant la fonction "tout réparer" au besoin "juste pour être du bon côté".
La deuxième approche est particulièrement agréable lorsque l'État peut provenir de différentes sources, par exemple de l'entrée utilisateur par rapport au rendu du serveur. Dans ASP.NET, la technique fonctionne très bien avec le concept de publication car vous exécutez simplement la fonction de correction de tout lorsque vous rendez la page.
Maintenant que j'ai mentionné que des événements étaient perdus ou répétés et obtenaient un état de différentes sources, je pense qu'il est évident que cette approche correspond bien à un espace problématique comme BitTorrent.
Les inconvénients? Eh bien, le con évident est qu'il y a un impact sur les performances car il est moins efficace de tout répéter tout le temps. Mais une solution comme BitTorrent est optimisée pour évoluer, pas évoluer, donc c'est bon pour ce genre de chose. Selon le problème que vous essayez de résoudre, il peut ne pas vous convenir.
Je pense que l'article est un peu daté car, à ma lecture, ce n'est pas du tout une idée peu orthodoxe ou nouvelle. Cette idée est présentée comme un modèle distinct alors qu'il ne s'agit en réalité que d'une simple implémentation d'Observer. En repensant à ce que je faisais à l'époque, je me souviens avoir travaillé sur la logique pour m'asseoir derrière une interface quelque peu complexe avec un certain nombre de panneaux différents avec des données interdépendantes. L'utilisateur pouvait modifier les valeurs et/ou exécuter une routine d'optimisation et en fonction de ces actions, des événements étaient générés que l'interface utilisateur écoutait et mettait à jour si nécessaire. Il y avait un certain nombre de problèmes pendant le développement où certains panneaux ne se mettaient pas à jour quand ils le devraient. Le correctif (en restant dans la conception) était de générer des événements à partir d'autres événements. En fin de compte, au moment où tout fonctionnait bien, presque chaque changement entraînait le rafraîchissement de tous les panneaux. Toute la complexité d'essayer d'isoler lorsqu'un panneau donné devait être actualisé était inutile. Et ça n'avait pas d'importance de toute façon. C'était effectivement une optimisation prématurée. J'aurais économisé une tonne de temps et d'efforts en regroupant tout simplement en un seul événement qui a tout rafraîchi.
Il existe d'innombrables systèmes conçus dans le "tout réparer" ou tout rafraîchir. Pensez à toutes les interfaces CRUD qui ajoutent/mettent à jour une ligne, puis réinterrogez la base de données. Ce n'est pas une approche exotique, c'est juste la solution non intelligente évidente. Vous devez vous rendre compte qu'en 2003, c'était le comble de la "fièvre modèle". D'après ce que je pouvais dire, les gens pensaient que nommer de nouveaux modèles allait être leur chemin vers la gloire et la richesse. Ne vous méprenez pas, je pense que le concept d'un modèle est extrêmement utile pour décrire des solutions dans l'abstrait. Les choses ont juste un peu explosé le Rails un peu. C'est dommage car cela a créé beaucoup de cynisme à propos du concept de modèle en général. Ce n'est que dans ce contexte qu'il est logique d'en parler comme une solution "peu orthodoxe". Elle est similaire à l'orthodoxie des ORM ou des conteneurs DI. Ne pas les utiliser est considéré comme peu orthodoxe même si les gens avaient construit des logiciels bien avant que ces outils n'existent et dans de nombreux cas, ces outils sont exagérés.
Revenons donc à "tout réparer". Un exemple simple est le calcul des moyens. La solution simple consiste à additionner les nombres et à les diviser par la cardinalité des valeurs. Si vous ajoutez ou modifiez un numéro, vous recommencez, depuis le début. Vous pouvez suivre la somme et le nombre de nombres et lorsque quelqu'un ajoute un nombre, vous augmentez le nombre et l'ajoutez à la somme. Maintenant, vous ne rajoutez pas tous les numéros à nouveau. Si vous avez déjà travaillé avec Excel avec une formule qui fait référence à une plage et que vous avez modifié une seule valeur dans cette plage, vous avez un exemple du modèle `` tout réparer '', c'est-à-dire que toute formule qui fait référence à cette plage recalculera indépendamment du fait que cette valeur était pertinente (par exemple en utilisant quelque chose comme sumif ()).
Cela ne veut pas dire que ce n'est pas un choix intelligent dans un contexte donné. Dans l'exemple moyen, disons que nous devons maintenant prendre en charge les mises à jour. Maintenant, j'ai besoin de connaître l'ancienne valeur et de ne changer la somme que par le delta. Rien de tout cela n'est vraiment difficile jusqu'à ce que vous envisagiez de le faire dans un environnement distribué ou simultané. Vous devez maintenant gérer toutes sortes de problèmes de synchronisation épineux et vous finirez probablement par créer un goulot d'étranglement majeur qui ralentit beaucoup plus que le recalcul.
Le résultat ici est que l'approche "tout réparer" ou "tout rafraîchir" est beaucoup plus facile à bien faire. Vous pouvez faire fonctionner une approche plus sophistiquée, mais c'est beaucoup plus compliqué et donc plus susceptible d'être imparfait. De plus, dans de nombreux contextes, l'approche "tout rafraîchir" peut être plus efficace. Par exemple, les approches de copie en écriture sont généralement plus lentes pour les approches monothread, mais lorsque vous avez une concurrence élevée, cela peut vous permettre d'éviter les verrous et donc de fournir de meilleures performances. Dans d'autres cas, il peut vous permettre de regrouper les modifications par lots de manière efficace. Donc, pour la plupart des problèmes, vous voudrez probablement commencer par une approche de tout rafraîchir, sauf si vous avez une raison spécifique pour laquelle vous ne pouvez pas le faire, puis vous inquiétez de faire quelque chose de plus complexe une fois que vous avez un besoin. Une implémentation fonctionnelle avec laquelle vous pouvez effectuer un test de régression est valable en soi, même si elle est lente.
Je ne sais pas si c'est un "modèle de conception", mais je classerais ce type de comportement comme configuration de l'état final ou configuration de l'état souhaité, dans la veine de Puppet, Chef ou Powershell DSC .
Ces solutions opèrent généralement au niveau de la gestion des systèmes, et non au niveau de la logique métier comme le décrit la question, mais il s'agit en fait du même paradigme, et bien que ces outils soient généralement de nature déclarative, les mêmes principes peuvent être appliqués dans le code procédural ou les scripts.
J'ai surtout utilisé cela dans les interfaces utilisateur. La bonne chose est que vous l'écrivez une fois, et il gère tout aussi bien du cas le plus simple au cas le plus difficile (par exemple si l'utilisateur fait pivoter l'écran, ou sur un ordinateur portable/de bureau si l'utilisateur redimensionne une fenêtre, et pratiquement tout change ).
Il n'y a pas beaucoup de raisons de s'inquiéter de l'efficacité. Dans l'interface utilisateur, les choses coûteuses sont des choses comme redessiner un élément qui a été déplacé. Le calcul de l'emplacement de chaque article et de sa taille est généralement assez rapide. Tout ce que vous devez vous assurer, c'est que chaque fois qu'un élément doit rester exactement à l'endroit où il appartient, aucun code n'est exécuté pour le déplacer. Les vrais changements sont toutes choses que vous deviez faire de toute façon.
Cela ressemble à des principes de programmation réactive. "Réparer tout" examine l'état "noyau" actuel et propage tout le reste qui devrait être affecté - "états calculés". Si vous optimisez cette dérivation, elle peut atteindre une efficacité élevée, a-la React, si elle est réalisée naïvement, les performances peuvent ne pas être optimales bien qu'elles puissent toujours être assez rapides.