Question

Il est généralement admis que la programmation par copier-coller est une mauvaise idée, mais quel est le meilleur moyen de gérer une situation dans laquelle vous avez deux fonctions ou blocs de code qui do doivent vraiment être différents quelques moyens rendent leur généralisation extrêmement désordonnée?

Que se passe-t-il si le code est sensiblement identique, à l'exception de quelques variations mineures, mais ces quelques variations mineures ne sont pas des éléments faciles à décomposer en ajoutant un paramètre, des méthodes de modèle ou quelque chose du genre? <

Plus généralement, avez-vous déjà rencontré une situation dans laquelle vous avoueriez qu'un petit codage copier-coller était vraiment justifié.

Était-ce utile?

La solution

J'ai entendu des gens dire qu'ils copieraient et colleraient une fois (limiter la duplication de code à au plus deux instances), car les abstractions ne rapportent rien à moins que vous n'utilisiez le code à trois endroits ou plus. () Moi-même, j'essaie de prendre l'habitude de refactoriser dès que j'en vois la nécessité.

Autres conseils

Posez cette question sur vos fonctions

"Si cette petite exigence change, devrai-je modifier les deux fonctions afin de la satisfaire?"

Bien sûr que c'est parfois acceptable. C'est pourquoi les gens gardent des fichiers d'extraits. Mais si vous coupez et collez du code très souvent, ou avec plus de quelques lignes, vous devriez penser à en faire un sous-programme. Pourquoi? parce que les chances sur vous devrez changer quelque chose, et de cette façon, vous ne devez le changer qu'une seule fois.

La casse centrale consiste à utiliser une macro si vous en avez.

Oui, et c'est exactement comme vous le dites. variations mineures mais difficiles à factoriser. Ne vous flagellez pas si c'est vraiment ce que la situation appelle.

Re Est-il toujours possible de couper les passes:

Oui. Lorsque le segment est légèrement différent et que vous utilisez des systèmes à usage unique (systèmes qui sont là depuis très peu de temps et ne nécessitent aucune maintenance). Sinon, il vaut généralement mieux extraire les points communs.

Les segments qui se ressemblent mais ne se ressemblent pas:

Si la différence est dans les données, refactorisez-la en extrayant la fonction et en utilisant la différence dans les données comme paramètres (s'il y a trop de données à passer en paramètre, envisagez de les regrouper dans un objet ou une structure). Si la différence est dans un processus dans la fonction, refactoriser en utilisant un modèle de commande ou un modèle abstrait. S'il est toujours difficile de refactoriser même avec ces modèles de conception, votre fonction essaiera peut-être d'assumer seule de nombreuses responsabilités.

Par exemple, si vous avez un segment de code qui diffère en deux segments - diff # 1 et diff # 2. Et dans diff # 1, vous pouvez avoir diff1A ou diff1B, et pour diff # 2 vous pouvez avoir diff2A et diff2B.

Si diff1A & amp; diff2A sont toujours ensemble et diff1B & amp; diff2B sont toujours ensemble, alors diff1A & amp; diff2A peut être contenu dans une classe de commande ou dans une implémentation de modèle abstrait, et diff1B & amp; diff2B dans un autre.

Toutefois, s’il existe plusieurs combinaisons (diff1A & diff2A, diff1A & diff2B, diff1B & diff2A, diff1B & diff2B), vous voudrez peut-être repenser votre fonction car elle essaiera peut-être de trop en gérer responsabilités en soi.

Instructions SQL:

Utiliser des logiques (if-else, boucles) pour construire votre code SQL sacrifie de façon dynamique la lisibilité. Mais créer toutes les variantes SQL serait difficile à maintenir. Alors rendez-vous à mi-chemin et utilisez les segments SQL. Extrayez les points communs en tant que segments SQL et créez toutes les variantes SQL avec ces segments SQL en tant que constantes.

Par exemple:

private static final String EMPLOYEE_COLUMNS = " id, fName, lName, status";

private static final String EMPLOYEE_TABLE = " employee";

private static final String EMPLOYEE_HAS_ACTIVE_STATUS = " employee";

private static final String GET_EMPLOYEE_BY_STATUS =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + EMPLOYEE_HAS_ACTIVE_STATUS;

private static final String GET_EMPLOYEE_BY_SOMETHING_ELSE =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + SOMETHING_ELSE;

Dans le code de base de mon entreprise, nous avons une série d’environ 10 énormes instructions SQL poilues qui présentent un degré élevé de similitude. Toutes les déclarations ont un noyau commun, ou du moins un noyau qui ne diffère que d'un mot ou deux. Ensuite, vous pouvez regrouper les 10 instructions en 3 ou 4 groupes qui ajoutent des annexes communes au noyau, encore une fois avec peut-être un ou deux mots différents dans chaque annexe. Quoi qu’il en soit, imaginez que les 10 instructions SQL soient définies dans un diagramme de Venn avec un chevauchement important.

Nous avons choisi de coder ces instructions de manière à éviter toute duplication. Donc, il existe une fonction (techniquement, une méthode Java) pour construire l’instruction. Il faut quelques paramètres qui expliquent le mot ou deux de différence dans le noyau commun. Ensuite, il faut un foncteur pour construire les appendices, qui est bien sûr également paramétré avec plus de paramètres pour les différences mineures et plus de foncteurs pour plus d’appendices, etc.>.

Le code est intelligent en ce sens qu'aucun des SQL n'est répété. Si vous avez besoin de modifier une clause dans le code SQL, vous la modifiez à un seul endroit et les 10 instructions SQL sont modifiées en conséquence.

Mais man est le code difficile à lire. La seule façon de savoir quel code SQL va être exécuté pour un cas donné consiste à exécuter un débogueur et à imprimer le code SQL une fois celui-ci complètement assemblé. Et comprendre comment une fonction particulière qui génère une clause s’intègre dans l’ensemble du tableau est désagréable.

Depuis que j'ai écrit ceci, je me suis souvent demandé si nous aurions mieux fait de copier-coller 10 fois la requête SQL. Bien entendu, si nous procédions ainsi, toute modification du code SQL pourrait devoir être effectuée à 10 endroits, mais les commentaires pourraient nous aider à nous indiquer les 10 endroits à mettre à jour.

L’avantage d’avoir le code SQL compréhensible et cohérent au même endroit l’emporterait probablement sur les inconvénients du copier-coller du code SQL.

Comme le suggère Martin Fowler,

faites-le une fois, d'accord.

faites-le deux fois, commence à sentir.

Faites-le trois fois, il est temps de refactor .

EDIT: en réponse au commentaire, l’origine du conseil est Don Roberts:

Trois coups et votre refactor .

Martin Fowler décrit cela dans la refactorisation du chapitre 2, section La règle de trois (page 58).

ABSOLUMENT NEEEVER ..

:)

Vous pouvez poster le code en question et constater qu'il est plus facile que ce à quoi il ressemble

Si c'est le seul moyen de le faire, allez-y. Souvent (selon la langue), vous pouvez apporter des modifications mineures à la même fonction avec un argument optionnel.

Récemment, j'avais une fonction add () et une fonction edit () dans un script PHP. Ils ont tous deux pratiquement fait la même chose, mais la fonction edit () a exécuté une requête UPDATE au lieu d'une requête INSERT. Je viens de faire quelque chose comme

function add($title, $content, $edit = false)
{
    # ...
    $sql = edit ? "UPDATE ..." : "INSERT ...";
    mysql_unbuffered_query($sql);
}

Très bien travaillé - mais il existe d'autres moments où copier / coller est nécessaire. N'utilisez pas un chemin étrange et compliqué pour l'empêcher.

  1. Un bon code est un code réutilisable.
  2. Ne réinventez pas la roue.
  3. Les exemples existent pour une raison: vous aider à apprendre et, idéalement, à mieux coder.

Devriez-vous copier et coller? On s'en fout! Ce qui est important, c’est pourquoi vous copiez et collez. Je n'essaie pas de faire de la philosophie à qui que ce soit ici, mais réfléchissons-y concrètement:

Est-ce par paresse? "Blah blah, je l'ai déjà fait auparavant ... Je ne change que quelques noms de variables ... done."

Ce n’est pas un problème si c’était déjà du bon code avant de l’avoir copié et collé. Sinon, vous perpétuez un code merdique par paresse qui vous mordra le cul sur la route.

Est-ce parce que vous ne comprenez pas? "Bon sang. Je ne comprends pas comment cette fonction fonctionne, mais je me demande si ça fonctionnera dans mon code .." Ça pourrait! Cela peut vous faire gagner du temps au moment où vous êtes stressé par le fait que vous avez une heure limite à 9 heures et que vous avez les yeux rouges sur une horloge vers 4 heures du matin.

Voulez-vous comprendre ce code lorsque vous y reviendrez? Même si tu le commentes? Non, vraiment - après des milliers de lignes de code, si vous ne comprenez pas ce que le code fait en l'écrivant, comment allez-vous comprendre qu'il reviendra des semaines, des mois plus tard? Essayez de l'apprendre, malgré toutes les tentations. Tapez-le, cela aidera à le mettre en mémoire. Chaque ligne que vous tapez, demandez-vous ce que cette ligne fait et comment elle contribue à l'objectif général de cette fonction. Même si vous ne l'apprenez pas à fond, vous pourrez peut-être le reconnaître au moins lorsque vous y reviendrez plus tard.

Alors - copier et coller du code? Parfait si vous êtes conscient des implications de ce que vous faites. Autrement? Ne le fais pas. Assurez-vous également que vous disposez d'une copie de la licence de tout code tiers que vous copiez et collez. Cela semble logique, mais vous seriez surpris de voir combien de personnes ne le font pas.

J'évite de couper et coller comme la peste. C'est encore pire que son cousin cloner et modifier. Si je suis confronté à une situation comme la votre, je suis toujours prêt à utiliser un processeur de macros ou un autre script pour générer les différentes variantes. D'après mon expérience, un seul point de vérité est extrêmement important.

Malheureusement, le processeur de macros C n’est pas très performant à cet égard en raison des exigences énormes en matière de citations pour les retours à la ligne, les expressions, les instructions et les arguments. Je déteste écrire

#define RETPOS(E) do { if ((E) > 0) then return; } while(0)

mais cette citation est une nécessité. J'utiliserai souvent le préprocesseur C malgré ses défauts, car il n'ajoute pas d'élément à la chaîne d'outils et ne nécessite donc pas de modification du processus de construction ou des Makefiles.

Je suis heureux que celui-ci soit étiqueté comme subjectif, car il l'est certainement! C'est un exemple trop vague, mais j'imagine que si vous avez assez de code dupliqué, vous pouvez extraire ces sections et conserver les différentes parties différentes. Le point de ne pas copier-coller est de ne pas avoir de code difficile à maintenir et fragile.

La meilleure façon (en dehors de la conversion en fonctions courantes ou de l'utilisation de macros) est d'insérer des commentaires. Si vous indiquez à quel endroit le code est copié et ce que sont les points communs, les différences et la raison de le faire ... alors tout ira bien.

Si vous constatez que vos fonctions sont généralement identiques, mais que, dans des scénarios différents, vous devez apporter de légers ajustements, c’est votre conception qui pose problème. Utilisez le polymorphisme et la composition au lieu des drapeaux ou du copier-coller.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top