La violation du principe DRY est-elle toujours mauvaise?

J’ai discuté du principe DRY ( Don’t Repeat Yourself ) également connu sous le nom de DIE ( Duplication Is Evil ) et il y a des votes, que toute répétition de code simple est toujours un mal. J’aimerais connaître votre opinion sur les points suivants:

  1. Un avenir incertain . Disons que nous avons le même code à deux endroits. La clé est que ces deux lieux n’ont qu’une connotation fortuite. Il est possible qu’elles varient dans le futur car leur contexte et leur sémantique sont différents. Faire une abstraction de ces endroits n’est pas bon marché et si l’un de ces endroits change, déballer de l’abstraction sera encore plus coûteux.
  2. Lisibilité . Il existe un calcul complexe impliquant plusieurs variables ou étapes. À un autre endroit du code, il y en a un autre, dont certaines parties sont identiques. Le problème est que si nous retirons les parties communes, la lisibilité du calcul diminuera et la création de l’abstraction sera très difficile à donner à un nom descriptif. Pire encore, si une partie de l’algorithme changera dans le futur comme au point 1.

Les cas ci-dessus sont-ils une bonne raison de renoncer au processus d’abstraction et de laisser simplement un code dupliqué en faveur du risque de modifications futures ou simplement de la lisibilité?

Ce sont des raisons tout à fait valables de violer le SEC. Je devrais append un troisième: performance. C’est rarement une grosse affaire, mais cela peut faire la différence, et l’abstraction risque de ralentir les choses.

En fait, j’appendai un quasortingème: perdre du temps et potentiellement introduire de nouveaux bogues en changeant deux parties (ou plus) d’une base de code qui pourrait déjà fonctionner correctement. Est-ce que cela vaut le coût de trouver comment faire de l’abstrait si cela n’est pas nécessaire et que cela ne sauvera probablement pas beaucoup de temps à l’avenir?

En règle générale, le code dupliqué n’est pas idéal, mais il existe certainement des raisons impérieuses de l’autoriser, y compris probablement d’autres raisons que ce que le PO et moi-même avons suggéré.

Oui, certaines duplications de code sont notoirement difficiles à prendre en compte sans aggraver la lisibilité. Dans de telles situations, je laisse un TODO dans les commentaires pour rappeler qu’il ya un certain dédoublement mais au moment de la rédaction, il semblait préférable de le laisser comme ça.

Habituellement, ce qui se passe est ce que vous écrivez dans votre premier point, les duplications divergent et ne sont plus des duplications. Il arrive également que la duplication soit le signe d’un problème de conception, mais elle ne se manifeste plus tard.

Longue histoire courte: essayez d’éviter la duplication; Si la duplication est notoirement difficile à prendre en compte et au moment de la rédaction inoffensive, il suffit de laisser un commentaire en guise de rappel.


Voir aussi 97 choses que chaque programmeur doit savoir :

p. 14. Attention au partage par Udi Dahan

Le fait que deux parties très différentes du système exécutaient une certaine logique de la même manière signifiait moins que je ne le pensais. Jusqu’à ce que je retire ces bibliothèques de code partagé, ces parties ne dépendaient pas l’une de l’autre. Chacun pourrait évoluer indépendamment. Chacun pourrait modifier sa logique pour répondre aux besoins de l’environnement commercial changeant du système. Ces quatre lignes de code similaire étaient accidentelles – une anomalie temporelle, une coïncidence.

Dans ce cas, il a créé une dépendance entre deux parties du système mieux conservées. La solution était essentiellement la duplication.

Essayons de comprendre pourquoi DRY est important, et alors nous pouvons comprendre où il est raisonnable de ne pas respecter la règle:

DRY devrait être utilisé pour éviter la situation où deux parties de code effectuent conceptuellement une partie du même travail. Par conséquent, chaque fois que vous modifiez le code à un endroit, vous devez changer le code à l’autre endroit. Si la même logique se trouve à deux endroits différents, vous devez toujours vous rappeler de changer la logique aux deux endroits, ce qui peut être source d’erreurs. Cela peut s’appliquer à n’importe quelle échelle. Il peut s’agir d’une application entière en cours de duplication ou d’une seule valeur constante. Il peut également ne pas y avoir de code répété, cela peut être un principe répété. Vous devez vous demander: «Si je devais effectuer un changement en un seul endroit, aurais-je nécessairement besoin de faire un changement équivalent ailleurs? Si la réponse est “oui”, le code viole DRY.

Imaginez que vous avez une ligne comme celle-ci dans votre programme:

 cost = price + price*0.10 // account for sales tax 

et ailleurs dans votre programme, vous avez une ligne similaire:

 x = base_price*1.1; // account for sales tax 

Si la taxe de vente change, vous devrez modifier ces deux lignes. Il n’y a presque pas de code répété ici, mais le fait que si vous effectuez un changement à un endroit nécessite un changement à un autre endroit, c’est ce qui fait que le code n’est pas SEC. De plus, il peut être très difficile de réaliser que vous devez faire le changement à deux endroits. Peut-être que vos tests unitaires vont l’attraper, mais peut-être pas, donc il est important de se débarrasser de la duplication. Vous pourriez peut-être prendre en compte la valeur de la taxe de vente dans une constante distincte pouvant être utilisée à plusieurs endroits:

 cost = price + price*sales_tax; x = base_price*(1.0+sales_tax); 

ou peut-être créer une fonction pour l’abstraire encore plus:

 cost = costWithTax(price); x = costWithTax(base_price); 

De toute façon, il est très probable que cela en vaille la peine.

Alternativement, vous pouvez avoir un code qui ressemble beaucoup mais ne viole pas DRY:

 x = base_price * 1.1; // add 10% markup for premium service 

Si vous modifiiez la façon dont la taxe de vente est calculée, vous ne voudriez pas modifier cette ligne de code, elle ne répète donc pas de logique.

Il y a aussi des cas où il faut faire le même changement à plusieurs endroits. Par exemple, vous avez peut-être un code comme celui-ci:

 a0 = f(0); a1 = f(1); 

Ce code n’est pas DRY de plusieurs manières. Par exemple, si vous modifiez le nom de la fonction f , vous devrez changer deux endroits. Vous pourriez peut-être rendre le code plus DRY en créant une petite boucle et en transformant a tableau en tableau. Cependant, cette duplication particulière n’est pas un gros problème. Premièrement, les deux changements sont très proches les uns des autres, il est donc peu probable que l’on change accidentellement un sans changer l’autre. Deuxièmement, si vous êtes dans un langage compilé, le compilateur sera probablement le problème le plus probable. Si vous n’êtes pas dans un langage compilé, alors, espérons que vos tests unitaires l’attraperont.

Il y a beaucoup de bonnes raisons de rendre votre code SEC, mais il y a beaucoup de bonnes raisons pour ne pas le faire.

L’ingénierie concerne les compromis, il n’y a donc pas de modèle de conception ou de conseil définitif valable pour chaque problème. Certaines décisions sont plus difficiles à prendre en charge que d’autres (la répétition du code en est une), mais si les avantages de la répétition du code l’emportent sur vos inconvénients dans votre cas, optez pour cette solution.

Il n’y a pas d’absolu, cela va toujours être un jugement entre le moindre des deux maux. Habituellement, DRY gagne et vous devez faire attention aux pentes glissantes lorsque vous commencez à le violer, mais votre raisonnement me semble correct.

Pour une excellente réponse à cette question, veuillez vous référer à “The Pragmatic Programmer” de Thomas, Hunt (c’est Dave Thomas qui a proposé le terme “Dry” en premier lieu).

En bref, il n’y a pas de réponse facile, il est presque toujours préférable de restr au sec, mais si cela améliore la lisibilité, alors vous devriez utiliser votre meilleur jugement, c’est votre appel!

Non, la violation de DRY n’est pas toujours mauvaise. En particulier, si vous ne parvenez pas à trouver une dénomination pour une abstraction du code dupliqué, c’est-à-dire un nom qui convient aux deux contextes, il se peut qu’il s’agisse de choses différentes et qu’il faut les laisser en double.

Dans mon expérience, ce type de coïncidence est plutôt rare, et plus le code dupliqué est grand, plus il est probable qu’il décrive un seul concept.

Je trouve aussi que l’ abstrait de la composition est presque toujours une meilleure idée à cet égard que l’ abstrait de l’inheritance, ce qui peut facilement conduire à de fausses équations et à des violations du LSP et du FAI .

Je crois que oui. Bien qu’en règle générale DRY soit idéal, il est parfois préférable de vous répéter. Je me retrouve souvent à ignorer le DRY lors de la phase de test préalable au développement. Vous ne savez jamais quand vous devrez apporter de légères modifications à une fonction que vous ne voulez pas modifier dans une autre. J’essaie bien sûr de toujours observer les applications DRY sur les applications “finies” (applications terminées et qui n’auront JAMAIS besoin d’être modifiées), mais celles-ci sont rares. En fin de compte, cela dépend des besoins futurs des applications. J’ai fait des applications que j’aurais aimé être SEC, et j’ai remercié Dieu de ne pas l’avoir observé sur d’autres.