Mon code doit-il être SEC ou lisible s'il ne peut pas être les deux?

14

J'écris du code Ruby pour un exercice de chiffrement simple et j'ai souvent rencontré ce dilemme (l'exercice est un chiffre solitaire si vous devez le savoir). Il s'agit de savoir si je devrais compléter ma logique avec des variables descriptives et des instructions en une seule étape qui rendent la fonction lisible au lieu d'une instruction concise, voire dense, qui élimine la répétition et / ou minimise les risques d'erreurs.

Mon exemple le plus récent: mon programme accepte les entrées et, en raison de directives de format rigides, il peut facilement déterminer si les entrées doivent être chiffrées ou déchiffrées. Pour simplifier, une fois que la clé de cryptage et le message sont convertis / générés pour être compatibles, il s'agit de soustraire la clé du message crypté ou d'ajouter la clé à un message non crypté, pour obtenir la sortie souhaitée (pensez à la clé comme cryptage, message + cryptage = code; code - cryptage = message). La position DRY me dit que je devrais convertir mon message crypté différemment de mon message non crypté afin que la fonction qui prend la clé de cryptage et l'applique au message n'a jamais besoin de faire de distinction. J'ai trouvé que cela signifie que j'ai besoin de quelques instructions imbriquées if dans la fonction, mais la logique semble solide. Ce code, cependant, n'est pas facilement lisible.

Je pourrais, d'autre part, écrire deux fonctions différentes qui sont appelées en fonction d'un indicateur qui est défini lorsque l'application détermine le chiffrement ou le déchiffrement. Ce serait plus simple à lire, mais cela dupliquerait la fonction de haut niveau consistant à appliquer la clé de chiffrement à un message (en le faisant être chiffré ou déchiffré).

Dois-je m'orienter vers un code lisible ou un code concis? Ou ai-je manqué une autre façon d'obtenir cette fonctionnalité et de satisfaire les deux principes? S'agit-il d'une position à une échelle dans laquelle il faut considérer l'objectif du projet et prendre les meilleures décisions pour atteindre cet objectif?

Jusqu'à présent, j'ai tendance à mettre l'accent sur le code SEC concis sur le code lisible.

lutze
la source
2
Utilisable en premier, SEC en second, Lisible en troisième. Un code hautement utilisable se traduit par un code consommateur hautement lisible qui répond plus facilement à SEC et lisible. C'est pourquoi vous voulez prendre des complexités désagréables et les emballer quelque part avec une belle API, si elles ne peuvent pas être améliorées; au moins le code qui interagit avec eux sera également épargné par cette approche.
Jimmy Hoffa
4
certains exemples de code réels peuvent aider, je soupçonne que vous manquez une troisième voie, comme des fonctions d'ordre supérieur pour avoir du code sec et lisible
jk.
2
Pas un doublon. Concise v. Lisible et DRY v. Lisible sont deux comparaisons très différentes. Ne pas être SEC est beaucoup plus dangereux que de ne pas être concis.
djechlin
Ne tombez pas dans le piège que supposer que le code fortement dupliqué est plus lisible car il tombe dans un modèle prévisible. Je pense qu'il peut être facile de penser de cette façon, jusqu'à ce que vous ayez maintenu un système qui se duplique si fortement que vous trouviez des bogues subtils dans une branche qui ne sont pas dans l'autre, des branches presque identiques.
KChaloux
Je ne comprends pas ce que vous entendez par "fonction de haut niveau consistant à appliquer la clé de chiffrement au message". Si vous voulez dire qu'il y a chevauchement entre chiffrer et déchiffrer, envisagez d'utiliser la refactorisation de la méthode d'extraction pour factoriser les parties communes.
Aaron Kurtzhals

Réponses:

20

SEC est une ligne directrice, pas une religion. Ceux qui le portent au point de DRY par-dessus tout, l'ont poussé trop loin.

Tout d'abord, le code utilisable est primordial. Si le code n'est pas utile et utilisable, il ne l'est pas ... et il est inutile de l'écrire en premier lieu.

Deuxièmement, un jour, quelqu'un devra maintenir votre code. Si votre code n'est pas maintenable, ils briseront votre "belle" conception dense, concise et sèche tout en maudissant votre nom. Ne fais pas ça. J'ai été cette personne et chaque fois que je vois un certain nom dans l'annotation du code, je frissonne.

Faire du code dense qui manque de "variables descriptives" et tout coller dans des expressions ternaires imbriquées avec lambdas sans aucune documentation est intelligent. Il est bon de savoir que vous pouvez le faire - mais ne le faites pas. Le code intelligent est très difficile à déboguer. Évitez d'écrire du code intelligent .

La plupart du temps consacré au logiciel est consacré à la maintenance du logiciel - et non à sa première écriture. Écrivez le code afin que vous (ou quelqu'un d'autre) puissiez corriger rapidement et facilement les bogues et y ajouter des fonctionnalités selon les besoins avec le moins de modifications de conception idéales.

Communauté
la source
Intuitivement ou non, vous avez rencontré un problème que je négligeais. J'ai écrit du code «intelligent» autant que possible pour cet exercice, juste pour savoir que je pouvais le faire, ce que je suis d'accord, c'est bien de savoir, mais je suis d'accord avec vous ici que c'est une mauvaise pratique. Maintenant, j'ai beaucoup de refactoring à faire.
lutze
1
@lutze Je suis un codeur Perl et parfois Ruby et je peux certainement comprendre la tentation d'y écrire du code intelligent. Une version pré-post de cette réponse comprenait une citation de Larry Wall sur l' orgueil - je crois que c'est une vertu très importante lorsque vous travaillez avec du code qui sera un jour maintenu. En raison de la complexité possible des langages, il est parfois difficile d'obtenir l'avantage d'écrire plus de code plutôt que d'essayer un code dense intelligent ... jusqu'à ce que vous deviez le maintenir.
17

Je ne suis pas sûr sur la base de la question que vous comprenez SEC. Le code DRY n'est pas le même que concis. C'est souvent le contraire.

Ici, je ne sais pas quel est le gros problème pour cet exemple. Créez une fonction pour crypter, une fonction pour décrypter, des assistants pour des fonctionnalités communes (mélanger des octets), et un simple frontal pour prendre des entrées et déterminer crypter / décrypter ... Pas de répétition.

En général cependant, DRY et la lisibilité existent pour aider à maintenir et à étendre le code. Tous les scénarios ne sont pas égaux, un gros coup de lisibilité pour supprimer une petite répétition n'est pas bon, ni un tas de duplication pour ajouter un peu de lisibilité.

Si vous appuyez dessus, je préférerais la lisibilité. Le code en double peut toujours être testé - un code illisible vous amène à faire (et à tester) la mauvaise chose.

Telastyn
la source
Grands points, j'avais en effet combiné un peu le principe DRY avec l'objectif d'un code concis.
lutze
12

Je ne suis pas familier avec votre cas spécifique, mais je peux donner quelques directives générales.

Le but de la lisibilité et de DRY est la maintenabilité .

La maintenabilité est importante dans la plupart des situations, vous passerez plus de temps à maintenir le code qu'à l'écrire. Cela est particulièrement vrai si vous considérez l'écriture de code comme un type spécial de maintenance.

Malheureusement, DRY est souvent mal compris. C'est en partie parce que cela semble si simple, mais comme beaucoup de choses simples, cela peut être, bien ... compliqué.

L'intention de DRY est que chaque unité de fonctionnalité existe en un seul endroit. Si le principe est respecté, le responsable du code chargé de modifier ou de vérifier que la fonctionnalité peut traiter le code en une seule fois. Si DRY n'est pas suivi, il existe un danger très réel qu'une certaine copie de la fonctionnalité ne soit pas maintenue correctement.

La violation la plus flagrante de DRY est le codage copier-coller, où des blocs entiers de code sont répétés textuellement dans la base de code. Ceci est étonnamment commun dans mon expérience, et aucun moyen qui ajoute à la lisibilité. Par conséquent, la refactorisation du code pour introduire une méthode commune augmente invariablement la conformité DRY et la lisibilité.

La deuxième violation la plus flagrante est "copier-coller et changer un peu". Encore une fois, la refactorisation pour introduire une méthode commune avec des paramètres, ou la décomposition d'une fonction en étapes et l'abstraction des points communs augmente presque toujours la lisibilité.

Ensuite, il y a les violations les plus subtiles, où la fonctionnalité est dupliquée mais le code est différent. Ce n'est pas toujours facile à repérer, mais lorsque vous le voyez et que vous refactorisez le code commun en une seule méthode / classe / fonction, le code est généralement plus lisible qu'auparavant.

Enfin, il y a les cas de répétition du design. Par exemple, vous avez peut-être utilisé le modèle d'état plusieurs fois dans votre base de code et vous envisagez une refactorisation pour éliminer cette répétition. Dans ce cas, procédez avec prudence. Vous réduisez peut-être la lisibilité en introduisant davantage de niveaux d'abstraction. En même temps, il ne s'agit pas vraiment de duplication de fonctionnalité mais de duplication d'abstraction. Parfois ça vaut le coup ... mais souvent ça ne l'est pas. Votre principe directeur sera de demander "lequel des deux est plus facile à maintenir".

Chaque fois que je fais ces appels au jugement, j'essaie de considérer le temps que les gens passeront à maintenir le code. Si le code est une fonctionnalité métier principale, il aura probablement besoin de plus de maintenance. Dans ce cas, je vise à rendre le code maintenable pour les personnes qui connaissent la base de code et les abstractions impliquées. Je serais plus heureux d'introduire un peu plus d'abstraction pour réduire la répétition. En revanche, un script qui est rarement utilisé et rarement maintenu peut être moins facile à saisir pour les responsables s'il implique trop d'abstraction. Dans ce cas, je me tromperais du côté de la répétition.

Je considère également le niveau d'expérience des autres membres de mon équipe. J'évite les abstractions "fantaisistes" avec des développeurs inexpérimentés, mais je mets à profit des modèles de conception reconnus par l'industrie avec des groupes plus matures.

En concision, la réponse à votre question est de faire tout ce qui rend votre code plus maintenable. Ce que cela signifie dans votre scénario est à vous de décider.

Kramii
la source
Je pense que vous avez résolu un de mes problèmes exactement. L'exemple que j'ai donné de la fonctionnalité que j'essayais de ne pas dupliquer est plus probablement une duplication d'abstraction.
lutze
+1. Je suis actuellement confronté à un système qui abuse du copier-coller à un degré ridicule. La suppression d'une seule méthode du code copié / collé a supprimé plus de 400 lignes de l'une de mes classes aujourd'hui. Malheureusement, je l'ai trouvé dans une méthode de 900 lignes ...
KChaloux
1
Si deux constructions de code font actuellement la même chose, mais un changement à l'un ne devrait généralement pas impliquer un changement à l'autre, le copier / coller peut être meilleur que la fonctionnalité commune factorisée. Si le code utilise deux méthodes identiques caractère par caractère faisant la même chose à des fins différentes et basant systématiquement le choix de la méthode sur le but requis, alors si les choses doivent être faites ultérieurement légèrement pour l'un de ces buts, en changeant juste le approprié affectera uniquement les comportements qui devraient être affectés. L'utilisation d'une seule méthode commune aurait pu rendre le changement beaucoup plus difficile.
supercat
7

Si vos fonctions deviennent plus compliquées et plus longues (donc moins lisibles) lorsque vous essayez de les rendre SÈCHES, vous vous trompez. Vous essayez de mettre trop de fonctionnalités dans une fonction car vous pensez que c'est le seul moyen d'éviter de répéter les mêmes morceaux de code dans une deuxième fonction.

Rendre le code SEC signifie presque toujours refactoriser des fonctionnalités communes vers des fonctions plus petites. Chacune de ces fonctions devrait être plus simple (et donc plus lisible) que l'original. Pour conserver tout dans la fonction d'origine au même niveau d'abstraction, cela peut également signifier de refactoriser des pièces supplémentaires qui ne sont pas utilisées ailleurs. Votre fonction d'origine devient alors une "fonction de haut niveau", appelant les plus petites, et elle devient aussi plus petite et plus simple.

Faire cela conduit par conséquent principalement à différents niveaux d'abstractions dans votre code - et certaines personnes pensent que ce type de code est moins lisible. D'après mon expérience, c'est une erreur. Le point clé ici est de donner aux petites fonctions de bons noms, d'introduire des types de données et des abstractions bien nommés qui peuvent être facilement saisis par une deuxième personne. De cette façon, "SEC" et "lisibilité" ne devraient entrer en conflit que très, très rarement.

Doc Brown
la source
2

Bien que je ne sois pas certain de la cause exacte du problème ici, mon expérience est que lorsque vous trouvez SEC et la lisibilité en conflit, cela signifie qu'il est temps de refactoriser quelque chose.

Loren Pechtel
la source
0

Je choisirais lisible (= maintenable) au lieu de SEC n'importe quel jour de la semaine. En réalité, les deux s'alignent souvent, quelqu'un qui comprend le concept de DRY produira généralement (principalement) du code lisible.

Zdravko Danev
la source
2
sans explication, cette réponse peut devenir inutile au cas où quelqu'un d'autre posterait une opinion contraire. Par exemple, si quelqu'un publie une affirmation du type "Je choisirais SEC (= maintenable) au lieu d'être lisible n'importe quel jour de la semaine", comment cette réponse aiderait-elle le lecteur à choisir entre deux opinions opposées? Envisagez de modifier dans une meilleure forme
moucher
0

Très très très très très peu de fois dans ma carrière j'ai trouvé un cas légitime qui opposait la lisibilité à DRY. Rendez-le lisible en premier. Nommez bien les choses. Copiez et collez si besoin.

Maintenant, reculez et regardez la totalité que vous avez créée, comme un artiste qui prend du recul pour regarder sa peinture. Vous pouvez maintenant identifier correctement la répétition.

Le code répété doit être soit refactorisé dans sa propre méthode, soit retiré dans sa propre classe.

La répétition du code devrait être un dernier recours. Lisable ET SEC d'abord, à défaut de cette lisibilité si vous limitez la portée du code répété.

Greg Burghardt
la source