Domanda

È generalmente accettato che la programmazione di copia e incolla è una cattiva idea, ma qual è il modo migliore per gestire una situazione in cui hai due funzioni o blocchi di codice che in realtà fanno devono essere diversi in solo alcuni modi per renderli estremamente disordinati?

Che cosa succede se il codice è sostanzialmente lo stesso, ad eccezione di alcune variazioni minori, ma quelle poche variazioni minori non si trovano in cose che sono facili da prendere in considerazione aggiungendo un parametro, metodi di modello o qualcosa del genere?

Più in generale, hai mai incontrato una situazione in cui avresti ammesso che un po 'di codice copia e incolla era veramente giustificato.

È stato utile?

Soluzione

Ho sentito gente dire che copieranno e incolleranno una volta (limitando la duplicazione del codice al massimo a due casi), poiché le astrazioni non pagano se non si utilizza il codice in tre o più posizioni. () Io stesso, provo a renderlo una buona abitudine di refactoring non appena ne vedo la necessità.

Altri suggerimenti

Poni questa domanda sulle tue funzioni

" se questo piccolo requisito cambia, dovrò cambiare entrambe le funzioni per soddisfarlo? "

Naturalmente a volte è accettabile. Ecco perché le persone conservano file di frammenti. Ma se stai tagliando e incollando il codice molto spesso, o con più di poche righe, dovresti pensare di renderlo una subroutine. Perché? perché su probabilità dovrai cambiare qualcosa, e in questo modo, dovrai cambiarlo solo una volta.

Il caso centrale è usare una macro se ne hai a disposizione.

Sì, ed è esattamente come dici tu; variazioni minori ma difficili da fatturare. Non ti flagellare se è davvero ciò che la situazione richiede.

Re È sempre accettabile:

Sì. Quando il segmento è leggermente diverso e stai facendo sistemi usa e getta (sistemi che sono lì per un periodo di tempo molto breve e non necessitano di manutenzione). Altrimenti, di solito è meglio estrarre i punti in comune.

Per segmenti che sembrano simili ma non esattamente uguali:

Se la differenza è nei dati, refactor estraendo la funzione e usando la differenza nei dati come parametri (Se ci sono troppi dati da passare come parametro, considera di raggrupparli in un oggetto o una struttura). Se la differenza è in qualche processo nella funzione, refactor usando il modello di comando o il modello astratto. Se è ancora difficile fare il refactoring anche con questi schemi di progettazione, allora la tua funzione potrebbe cercare di far fronte a molte responsabilità da sola.

Ad esempio, se hai un segmento di codice che differisce in due segmenti - diff # 1 e diff # 2. E in diff # 1, puoi avere diff1A o diff1B e per diff # 2 puoi avere diff2A e diff2B.

Se diff1A & amp; diff2A sono sempre insieme e diff1B & amp; diff2B sono sempre insieme, quindi diff1A & amp; diff2A può essere contenuto in una classe di comando o in un'implementazione astratta del modello e diff1B & amp; diff2B in un altro.

Tuttavia, se ci sono diverse combinazioni (es. diff1A & amp; diff2A, diff1A & amp; diff2B, diff1B & amp; diff2A, diff1B & amp; diff2B), allora potresti voler ripensare la tua funzione perché potrebbe provare a gestirne troppe responsabilità da sola.

Istruzioni Re SQL:

L'uso della logica (if-else, loop) per costruire il tuo SQL sacrifica dinamicamente la leggibilità. Ma creare tutte le varianti SQL sarebbe difficile da mantenere. Quindi incontra a metà strada e usa i segmenti SQL. Estrai elementi comuni come segmenti SQL e crea tutte le varianti SQL con quei segmenti SQL come costanti.

Ad esempio:

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;

Nella base di codice della mia azienda, abbiamo una serie di circa 10 dichiarazioni SQL pelose che hanno un alto grado di comunanza. Tutte le affermazioni hanno un nucleo comune, o almeno un nucleo che differisce solo per una parola o due. Quindi, è possibile raggruppare le 10 istruzioni in 3 o 4 raggruppamenti che aggiungono appendici comuni al nucleo, sempre con forse una o due parole diverse in ciascuna appendice. Ad ogni modo, pensa alle 10 istruzioni SQL come set in un diagramma di Venn con significative sovrapposizioni.

Abbiamo scelto di codificare queste istruzioni in modo tale da evitare qualsiasi duplicazione. Quindi, c'è una funzione (tecnicamente, un metodo Java) per costruire l'istruzione. Prende alcuni parametri che spiegano la parola o due di differenza nel core comune. Quindi, ci vuole un funzione per costruire le appendici, che ovviamente è anche parametrizzata con più parametri per differenze minori e più funzioni per più appendici, e così via.

Il codice è intelligente in quanto nessuno degli SQL viene mai ripetuto. Se hai mai bisogno di modificare una clausola in SQL, la modifichi in un solo posto e tutte le 10 istruzioni SQL vengono modificate di conseguenza.

Ma man è il codice difficile da leggere. L'unico modo per capire quale SQL verrà eseguito per un determinato caso è passare attraverso un debugger e stampare l'SQL dopo che è stato completamente assemblato. E capire come una particolare funzione che genera una clausola si adatta al quadro più ampio è sgradevole.

Da quando ho scritto questo, mi sono spesso chiesto se avremmo fatto meglio a tagliare e incollare la query SQL 10 volte. Ovviamente, se lo facessimo, potrebbe essere necessario apportare modifiche a SQL in 10 punti, ma i commenti potrebbero aiutarci a indicarci i 10 punti da aggiornare.

Il vantaggio di avere l'SQL comprensibile e tutto in un unico posto probabilmente supererebbe gli svantaggi del tagliare e incollare l'SQL.

Come suggerisce Martin Fowler,

fallo una volta, va bene.

fallo due volte, inizia a puzzare.

fallo tre volte, tempo di refactor .


EDIT: in risposta al commento, l'origine del consiglio è Don Roberts:

Tre colpi e tu rifletti .

Martin Fowler lo descrive nel Refactoring capitolo 2, sezione La regola dei tre (pagina 58).

ASSOLUTAMENTE NEEEVER ..

:)

Potresti pubblicare il codice in questione e vedere che è più facile di come appare

Se è l'unico modo per farlo, allora cercalo. Spesso (a seconda della lingua), puoi soddisfare piccole modifiche alla stessa funzione con un argomento facoltativo.

Di recente, ho avuto una funzione add () e una funzione edit () in uno script PHP. Entrambi hanno fatto praticamente la stessa cosa, ma la funzione edit () ha eseguito una query UPDATE anziché una query INSERT. Ho appena fatto qualcosa di simile

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

Ha funzionato alla grande, ma ci sono altre volte in cui è necessario copiare / incollare. Non utilizzare un percorso strano e complicato per impedirlo.

  1. Un buon codice è un codice riutilizzabile.
  2. Non reinventare la ruota.
  3. Gli esempi esistono per una ragione: per aiutarti ad imparare e idealmente codificare meglio.

Dovresti copiare e incollare? Che importa! Ciò che è importante è perché stai copiando e incollando. Non sto cercando di essere filosofico su nessuno qui, ma pensiamo a questo praticamente:

È per pigrizia? " Blah blah, l'ho già fatto ... Sto cambiando solo alcuni nomi di variabili ... fatto. "

Non è un problema se era già un buon codice prima di copiarlo e incollarlo. Altrimenti, stai perpetuando un codice schifoso per pigrizia che ti morderà il culo lungo la strada.

È perché non capisci? " Accidenti .. Non capisco come funziona quella funzione, ma mi chiedo se funzionerà nel mio codice .. " Potrebbe! Questo potrebbe farti risparmiare tempo nell'immediato momento in cui sei stressato di avere una scadenza alle 9 e stai fissando con gli occhi rossi un orologio intorno alle 4 del mattino

Capirai questo codice quando tornerai ad esso? Anche se lo commenti? No davvero - dopo migliaia di righe di codice, se non capisci cosa sta facendo il codice mentre lo scrivi come capirai di ritornarci settimane, mesi dopo? Tenta di impararlo, nonostante tutte le tentazioni altrimenti. Digitalo, questo ti aiuterà a impegnarlo in memoria. Ogni riga che digiti, chiediti cosa sta facendo quella linea e come contribuisce allo scopo generale di quella funzione. Anche se non lo impari a fondo, potresti avere la possibilità di riconoscerlo almeno quando torni più tardi.

Quindi - copiare e incollare il codice? Va bene se sei consapevole delle implicazioni di ciò che stai facendo. Altrimenti? Non farlo Inoltre, assicurati di avere una copia della licenza di qualsiasi codice di terze parti che copi e incolli. Sembra buon senso, ma rimarrai sorpreso da quante persone non lo fanno.

Evito di tagliare e incollare come la peste. È anche peggio del clone e della modifica di suo cugino. Di fronte a una situazione come la tua sono sempre pronto a utilizzare un processore macro o altri script per generare le diverse varianti. Nella mia esperienza un unico punto di verità è estremamente importante.

Sfortunatamente il processore macro C non è molto adatto a questo scopo a causa dei fastidiosi requisiti di quotazione per newline, espressioni, dichiarazioni e argomenti. Odio scrivere

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

ma quella citazione è una necessità. Userò spesso il preprocessore C nonostante le sue carenze perché non aggiunge un altro elemento alla toolchain e quindi non richiede la modifica del processo di creazione o dei Makefile.

Sono contento che questo sia etichettato come soggettivo perché sicuramente lo è! Questo è un esempio troppo vago, ma immagino che se si dispone di un codice sufficiente che viene duplicato, è possibile estrarre quelle sezioni e mantenere diverse le diverse parti. Il punto di non copiare e incollare è quindi non finire con un codice difficile da mantenere e fragile.

Il modo migliore (oltre a convertirsi in funzioni comuni o usare macro) è inserire commenti. Se commentate da dove viene copiato il codice, da quale è la comunanza, le differenze e il motivo per farlo ... allora starai bene.

Se scopri di avere funzioni che sono per lo più le stesse, ma in scenari diversi richiedono lievi modifiche, è il problema il tuo progetto. Usa il polimorfismo e la composizione invece di bandiere o copia-incolla.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top