Duplicazione del codice senza astrazione evidente

13

Hai mai incontrato un caso di duplicazione del codice in cui, guardando le righe di codice, non potresti adattare un'astrazione tematica ad essa che descrive fedelmente il suo ruolo nella logica? E cosa hai fatto per affrontarlo?

È la duplicazione del codice, quindi idealmente abbiamo bisogno di rifrattarlo, come per esempio renderlo una sua funzione. Ma dal momento che il codice non ha una buona astrazione per descriverlo, il risultato sarebbe una strana funzione per cui non possiamo nemmeno capire un buon nome, e il cui ruolo nella logica non è ovvio solo dal guardarlo. Questo, per me, ferisce la chiarezza del codice. Possiamo preservare la chiarezza e lasciarla così com'è, ma poi danneggiamo la manutenibilità.

Quale pensi che sia il modo migliore per affrontare qualcosa di simile?

    
posta EpsilonVector 25.10.2010 - 23:55
fonte

4 risposte

17

A volte la duplicazione del codice è il risultato di un "gioco di parole": due cose sembrano uguali, ma non lo sono.

È possibile che un eccesso di astrazione possa rompere la vera modularità del sistema. Sotto il regime della modularità, devi decidere "cosa è probabile che cambi?" e "cosa è stabile?". Qualunque cosa sia stabile viene inserita nell'interfaccia, mentre tutto ciò che è instabile viene incapsulato nell'implementazione del modulo. Quindi, quando le cose cambiano, la modifica che devi apportare è isolata per quel modulo.

Il refactoring è necessario quando ciò che pensavi fosse stabile (ad esempio, questa chiamata API avrà sempre due argomenti) deve essere modificato.

Quindi, per questi due frammenti di codice duplicati, vorrei chiedere: Una modifica richiesta per uno significa necessariamente che anche l'altro deve essere modificato?

Il modo in cui rispondi a questa domanda potrebbe fornirti una visione migliore di ciò che potrebbe essere una buona astrazione.

I modelli di progettazione sono anche strumenti utili. Forse il tuo codice duplicato sta eseguendo un attraversamento di qualche forma e dovrebbe essere applicato il pattern iteratore.

Se il tuo codice duplicato ha più valori di ritorno (ed è per questo che non puoi eseguire un semplice metodo di estrazione), forse dovresti creare una classe che contenga i valori restituiti. La classe potrebbe chiamare un metodo astratto per ogni punto che varia tra i due frammenti di codice. Dovresti quindi realizzare due concreti implementazioni della classe: una per ogni frammento. [Questo è effettivamente il modello di progettazione del metodo Template, da non confondere con il concetto di template in C ++. In alternativa, ciò che stai guardando potrebbe essere risolto meglio con il modello di strategia.]

Un altro modo naturale e utile per pensarci è con le funzioni di ordine superiore. Ad esempio, creando lambda o usando classi interne anonime affinché il codice passi all'astrazione. In generale, puoi rimuovere la duplicazione, ma a meno che non ci sia una relazione tra loro [se uno cambia, così anche l'altro], potresti danneggiare la modularità, non aiutarla.

    
risposta data 26.10.2010 - 00:13
fonte
4

Quando incontri una situazione come questa, è meglio pensare alle astrazioni "non tradizionali". Forse hai un sacco di duplicazioni all'interno di una funzione e il factoring di una semplice vecchia funzione non si adatta molto bene perché devi passare troppe variabili. Qui, una funzione annidata in stile D / Python (con accesso all'ambito esterno) funzionerebbe benissimo. (Sì, si potrebbe fare in modo che una classe mantenga tutto quello stato, ma se la si utilizza solo in due funzioni, questa è una brutta e prolissa soluzione per non avere funzioni nidificate.) Forse l'ereditarietà non è adatta, ma una il mixin funzionerebbe bene. Forse quello di cui hai veramente bisogno è una macro. Forse dovresti prendere in considerazione alcuni modelli di metaprogrammazione o riflessione / introspezione, o anche di programmazione generativa.

Naturalmente, da un punto di vista pragmatico, questi sono tutti difficili se non impossibili da fare se il tuo linguaggio non li supporta e non ha abbastanza capacità di metaprogrammazione per implementarli in modo pulito all'interno della lingua. Se questo è il caso, non so cosa dirti se non "avere una lingua migliore". Inoltre, l'apprendimento di un linguaggio di alto livello con molte capacità di astrazione (come Ruby, Python, Lisp o D) potrebbe aiutarti a programmare meglio nei linguaggi di livello inferiore in cui alcune tecniche potrebbero essere ancora utilizzabili, ma meno ovvie.

    
risposta data 26.10.2010 - 00:13
fonte
3

Personalmente lo ignoro e procedo. Le probabilità sono che se si tratta di un caso strano è meglio averlo duplicato, si potrebbe spendere di tempo refactoring e il prossimo sviluppatore darà un aspetto e annullerà il cambiamento!

    
risposta data 26.10.2010 - 00:04
fonte
1

Senza un esempio di codice, è difficile dire perché il tuo codice non ha un'astrazione prontamente identificabile. Con questo avvertimento, ecco un paio di idee:

  • invece di creare una nuova funzione per contenere il codice comune, suddividere la funzionalità in più parti distinte;
  • raggruppa piccoli pezzi in base a tipi di dati comuni o comportamenti astratti;
  • riscrivi il codice duplicato dato i nuovi pezzi;
  • se il nuovo codice continua a sfidare un'astrazione chiara, suddividilo in piccolo e ripeti il processo.

La più grande difficoltà in questo esercizio è che probabilmente la tua funzione incorpora troppi comportamenti non correlati a un determinato livello di astrazione e devi gestirne alcuni a livelli più bassi. Pretendete correttamente che la chiarezza è la chiave per mantenere il codice, ma rendere il comportamento del codice chiaro (la sua condizione attuale) è molto diverso dal rendere chiaro l'intento del codice.

Rendi astratto il codice dei pezzi di codice più piccoli facendo in modo che le loro firme di funzione identificano il cosa e i pezzi più grandi dovrebbero essere più facili da classificare.

    
risposta data 26.10.2010 - 00:39
fonte

Leggi altre domande sui tag