Domanda

Al momento sto rivedendo un vecchio C ++ progetto e vedere un sacco di duplicazione del codice lì.

Ad esempio, v'è una classe con 5 gestori di messaggi MFC ciascuna contenente 10 righe identiche di codice. O c'è un frammento di 5-line per un tempo molto preciso trasformazione stringa ogni qua e là. Ridurre la duplicazione del codice non è un problema in questi casi a tutti.

Ma ho la strana sensazione che avrei potuto essere equivoco qualcosa e che non vi era in origine una ragione per questo la duplicazione.

Quale potrebbe essere un motivo valido per la duplicazione di codice?

È stato utile?

Soluzione

Quando ho iniziato la programmazione, ho scritto un app dove ho avuto un sacco di funzionalità simile che ho avvolto in una piccola funzione 20-30 linea netta ... Ero molto orgoglioso di me stesso per aver scritto un pezzo di così elegante codice.

Poco dopo, il cliente ha cambiato il processo in casi molto specifici, poi di nuovo, poi di nuovo, poi ancora, e ancora, e ancora .... (molte molte altre volte) Il mio codice elegante trasformato in un molto difficile, hackish , buggy, e alta pasticcio di manutenzione.

Un anno dopo, quando mi è stato chiesto di fare qualcosa di molto simile, ho deliberatamente deciso di ignorare SECCO. Ho messo insieme il processo di base, e ha generato tutto il codice duplicato. Il codice duplicato è stato documentato e ho salvato il modello utilizzato per generare il codice. Quando il client chiesto specifica trasformazione condizionale (come, se x == y ^ z + b poi 1 + 2 == 3,42) era un pezzo di torta. E 'stato incredibilmente facile da mantenere e modificare.

In retrospettiva, probabilmente avrebbe potuto risolvere molti di questi problemi con puntatori a funzione e predicati, ma utilizzando le conoscenze che avevo in quel momento, ho ancora credere in questo caso specifico, questa è stata la decisione migliore.

Altri suggerimenti

Una buona lettura di questo è larga scala C ++ progettazione del software da Giovanni Lakos.

Ha molte cose positive di duplicazione di codice, in cui potrebbe aiutare o ostacolare un progetto.

Il punto più importante sta chiedendo quando si decide di rimuovere la duplicazione o duplicare il codice:

Se questo metodo modifiche in futuro, voglio modificare il comportamento nel metodo duplicato, o ha bisogno di rimanere così com'è?

Dopo tutto, i metodi contengono (business) la logica, e, talvolta, ti consigliamo di cambiare la logica per ogni chiamante, a volte no. Dipende dalle circostanze.

Alla fine, è tutta una questione di manutenzione, non si tratta di bella fonte.

Pigrizia, che è l'unica ragione che posso pensare.

Su una nota più grave. L'unico motivo valido che posso pensare è modifiche alla fine del ciclo di prodotto. Questi tendono a subire molto più controllo e il più piccolo cambiamento tende ad avere il più alto tasso di successo. In quella circostanza limitata è più facile per ottenere un codice di duplicazione cambiamento attraverso invece di refactoring un cambiamento più piccolo.

lascia ancora l'amaro in bocca.

Oltre ad essere inesperto, non v'è motivo per cui le occorrenze codice duplicato potrebbero presentarsi:

Non c'è tempo per refactoring correttamente

La maggior parte di noi stanno lavorando in un mondo reale in cui i vincoli reali ci costringono a muoversi rapidamente per problemi reali invece di pensare gentilezza del codice. Così abbiamo copia & incolla e andare avanti. Con me, se poi vedo che il codice viene duplicato diverse volte, è il segno che devo spendere un po 'di tempo su di esso e convergono tutte le istanze a uno.

La generalizzazione del codice non è possibile / non e 'abbastanza' a causa di vincoli di lingua

Diciamo che in profondità all'interno di una funzione sono disponibili diverse affermazioni che notevolmente diverse da caso a caso di stesso codice duplicato. Per esempio: Ho una funzione che disegna 2d serie di miniature per il video, ed è incorporato con calcolo di ciascuna posizione miniatura. Per calcolare colpire-test (calcolare l'indice delle miniature dalla posizione di scatto) Sto usando lo stesso codice, ma senza la pittura.

Non siete sicuri che non ci sarà generalizzazione a tutti

Duplica il codice in un primo momento, e poi osservare come si evolverà. Dal momento che stiamo scrivendo software, possiamo permettere 'il più tardi possibile' modifiche al software, dal momento che tutto è 'soft' e mutevole.

Io aggiungo altro se non ricordo altro.


aggiunto in seguito ...

Loop srotolando

Nel tempo prima che i compilatori erano intelligenti come combinati Einstein e Hawking, si doveva srotolare il loop o codice inline per essere più veloce. srotolamento Loop renderà il vostro codice da duplicato, e probabilmente più veloce da piccola percentuale, ma il compilatore non lo ha fatto per voi in ogni caso.

Si potrebbe desiderare di fare in modo di assicurarsi che i futuri cambiamenti di una parte non cambierà involontariamente, dall'altra. per esempio in considerazione

Do_A_Policy()
{
  printf("%d",1);
  printf("%d",2);
}

Do_B_Policy()
{
  printf("%d",1);
  printf("%d",2);
}

Ora è possibile impedire "la duplicazione del codice" con la funzione in questo modo:

first_policy()
{
printf("%d",1);
printf("%d",2);
}

Do_A_Policy()
{
first_policy()
}

Do_B_Policy()
{
first_policy()
}

Tuttavia c'è il rischio che qualche altro programmatore vuole cambiare Do_A_Policy () e lo farà cambiando first_policy () e provoca l'effetto collaterale di cambiare Do_B_Policy (), un effetto collaterale che il programmatore può non essere a conoscenza. quindi questo tipo di "duplicazione codice" può servire come un meccanismo di sicurezza contro questo tipo di future variazioni del programma.

A volte i metodi e le classi che hanno dominio-saggio nulla in comune, ma l'attuazione-saggio sembra molto simili. In questi casi è spesso meglio fare la duplicazione del codice come futuro cambia più spesso che non si diramano queste implementazioni in qualcosa che non sono gli stessi.

La ragione valida mi viene in mente: Se il codice diventa molto più complessa al fine di evitare la duplicazione. Fondamentalmente questo è il luogo in cui si fa quasi lo stesso in diversi metodi - ma semplicemente non proprio la stessa cosa. Naturalmente - si può quindi refactoring e aggiungere parametri speciali tra cui i puntatori ai diversi membri che devono essere modificati. Ma il nuovo, metodo di refactoring può diventare troppo complicata.

Esempio (pseudocodice):

procedure setPropertyStart(adress, mode, value)
begin
  d:=getObject(adress)
  case mode do
  begin
    single: 
       d.setStart(SingleMode, value);
    delta:
       //do some calculations
       d.setStart(DeltaSingle, calculatedValue);
   ...
end;

procedure setPropertyStop(adress, mode, value)
begin
  d:=getObject(adress)
  case mode do
  begin
    single: 
       d.setStop(SingleMode, value);
    delta:
       //do some calculations
       d.setStop(DeltaSingle, calculatedValue);
   ...
end;

Si potrebbe refactoring la chiamata al metodo (setXXX) in qualche modo - ma a seconda della lingua potrebbe essere difficile (soprattutto con l'ereditarietà). E 'la duplicazione del codice poiché la maggior parte del corpo è la stessa per ogni proprietà, ma può essere difficile refactoring le parti comuni.

In breve -. Se il metodo di refactoring è fattori più complicato, mi piacerebbe andare con la duplicazione del codice anche se è "il male" (e resterò male)

L'unica cosa "valido" posso vedere questo è derivante da quando quelle linee di codice erano diverse, quindi convergenti alla stessa cosa attraverso modifiche successive. Ho avuto questo accada a me prima, ma non troppo spesso.

Questo è, naturalmente, quando è il momento di scomporre questa sezione comune di codice in nuove funzionalità.

Detto questo, non riesco a pensare a un modo ragionevole per giustificare il codice duplicato. Guardate questo che è male.

E 'male, perché un cambiamento in un unico luogo richiede un cambiamento in più posti. Questo aumenta il tempo, con la possibilità di bug. Di factoring fuori, a mantenere il codice in un unico luogo di lavoro. Dopo tutto, quando si scrive un programma non si scrive due volte, perché sarebbe una funzione dovrebbe essere diverso?

Per quel tipo di duplicazione del codice (un sacco di linee duplicate un sacco di volte), direi:

  • o pigrizia (basta incollare un codice qua e là, senza doversi preoccupare di qualsiasi impatto che potrebbe avere su altre parti della domanda - durante la scrittura di una nuova funzione e di utilizzarlo in due posti potessi, supponiamo, un certo impatto)
  • o non conoscendo alcuna buona pratica (riutilizzo di codice, separazione compiti diversi in diverse funzioni / metodi)

Probabilmente la prima soluzione, anche se, da quello che ho visto in generale: - (

La soluzione migliore che ho visto contro che: hanno i tuoi sviluppatori iniziano mantenendo qualche vecchia applicazione, quando essi vengono assunti - che ti insegnano loro che questo genere di cose non è buona ... e saranno capire per questo, che è la parte più importante.

Codice dividendosi in diverse funzioni, ri-utilizzando il codice nel modo giusto, e tutto ciò che spesso vengono con l'esperienza - o non hanno assunto le persone giuste; -)

Molto tempo fa, quando ho usato per fare grafica che di programmazione sarebbe, in alcuni casi particolari, utilizzare il codice duplicato questo modo per evitare il basso livello dichiarazioni JMP generati nel codice (che sarebbe migliorare le prestazioni, evitando il salto per l'etichetta /funzione). E 'stato un modo per ottimizzare e fare una pseudo "inlining".

Tuttavia, in questo caso, non credo che sia il motivo per cui lo facevano, eh.

Se compiti diversi sono simili per caso, ripetendo le stesse azioni in due luoghi non è necessariamente la duplicazione. Se le azioni in un cambio posto, è probabile che dovrebbero cambiare in altri luoghi come bene? Allora questo è la duplicazione si dovrebbe evitare o refactoring di distanza.

Inoltre, a volte - anche quando la logica è duplicato - il costo di ridurre la duplicazione è troppo alta. Questo può accadere soprattutto quando non si tratta solo duplicazione del codice: ad esempio, se si dispone di un record di dati con alcuni campi che si ripete in luoghi diversi (DB definizione della tabella di classe, C ++, l'immissione di testo-base), il solito modo per ridurre questo duplicazione con generazione del codice. Questo aggiunge complessità alla soluzione. Quasi sempre, questa complessità paga, ma a volte non è così -. È il tuo compromesso per rendere

Non so di molte buone ragioni per la duplicazione del codice, ma invece di saltare in piedi prima di refactoring, è probabilmente meglio di refactoring solo i bit del codice che in realtà cambia, piuttosto che alterare una grande base di codice che si ancora non comprendere appieno.

Suona come l'autore originale o era inesperto e / o era molto difficile riuscire in tempo. La maggior parte programmatori esperti mucchio insieme le cose che vengono riutilizzati, perché poi ci sarà meno manutenzione -. Una forma di pigrizia

L'unica cosa che si dovrebbe verificare è se ci sono effetti collaterali, se il codice copiato accede alcuni dati globali un refactoring bit può essere necessario.

modifica di nuovo nel giorno in cui compilatori erano schifoso e ottimizzatori anche crappier potrebbe capitare che a causa di qualche bug nel compilatore si può doveva fare una trucchetto per aggirare un bug . Forse è qualcosa di simile? Quanti anni è vecchio?

Sui grandi progetti (quelli con un codice-base grande come un GB) E 'del tutto possibile per perdere API esistenti. Questo è in genere a causa di documentazione insufficiente o l'incapacità del programmatore per individuare il codice originale; quindi duplicare il codice.

si riduce a pigrizia, o scarsa pratica recensione.

EDIT:

Una ulteriore possibilità è che ci può essere stato codice addizionale in quei metodi che è stato rimosso lungo la strada.

Hai guardato la storia delle revisioni del file?

Tutte le risposte guarda a destra, ma penso che ci sia un'altra possibilità. Forse ci sono considerazioni sulle prestazioni come le cose che dici mi ricorda "il codice inline". E 'sempre più veloce a funzioni inline che chiamarli. Forse il codice si guarda è stato pre-elaborato prima?

Non ho problemi con il codice duplicato quando viene prodotta da un generatore di codice sorgente.

Una cosa che abbiamo scoperto che ci ha costretti a duplicare il codice è stato il nostro codice di manipolazione dei pixel. Noi lavoriamo con immagini molto grandi e il sovraccarico funzione di chiamata stava mangiando dell'ordine del 30% del nostro tempo per-pixel.

duplicare il codice di manipolazione dei pixel ci ha dato un'immagine 20% più veloce traversata a costo della complessità del codice.

Questo è ovviamente un caso molto raro, e alla fine è gonfio nostra fonte significativa (funzione 300 linea è ora 1200 linee).

Non c'è nessuna buona ragione per la duplicazione del codice.

Vedere la rel="nofollow refactoring senza pietà modello di progettazione.

Il programmatore originale era o in fretta per rispettare una scadenza o pigri. Sentitevi liberi di refactoring e migliorare il codice.

a mio modesto parere non c'è posto per la duplicazione del codice. dare un'occhiata, per esempio, a questo articolo wikipedia

o, facciamo riferimento a citazioni di Larry Wall:

  

"Vi incoraggio a sviluppare le tre grandi virtù di un programmatore:. Pigrizia, impazienza e la tracotanza"

E 'abbastanza chiaro che la duplicazione del codice non ha nulla a che fare con la "pigrizia". haha;)

Poiché non v'è il "modello di strategia", non v'è alcun motivo valido per il codice duplicato. Non una sola riga di codice deve essere duplicato, tutto il resto è fallimento epico.

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