Duplicazione del codice senza alcuna evidente astrazione


14

Ti è mai capitato di riscontrare un caso di duplicazione del codice in cui, osservando le linee di codice, non riuscivi a inserirvi un'astrazione tematica che descriva fedelmente il suo ruolo nella logica? E cosa hai fatto per affrontarlo?

È la duplicazione del codice, quindi idealmente dobbiamo fare un po 'di rifrazione, come ad esempio renderlo una sua funzione. Ma poiché il codice non ha una buona astrazione per descriverlo, il risultato sarebbe una strana funzione per la quale non riusciamo nemmeno a trovare un buon nome, e il cui ruolo nella logica non è ovvio solo dal guardarlo. Questo, per me, fa male alla chiarezza del codice. Possiamo preservare la chiarezza e lasciarla così com'è ma poi danneggiamo la manutenibilità.

Quale pensi sia il modo migliore per affrontare qualcosa del genere?

Risposte:


18

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

È possibile che l'eccessiva astrazione possa interrompere la vera modularità del sistema. Sotto il regime della modularità, devi decidere "che cosa potrebbe cambiare?" e "cos'è stabile?". Tutto ciò che è stabile viene inserito nell'interfaccia, mentre ciò che è instabile viene incapsulato nell'implementazione del modulo. Quindi, quando le cose cambiano, la modifica che devi apportare è isolata a quel modulo.

Il refactoring è necessario quando ciò che pensavi fosse stabile (ad es. Questa chiamata API prenderà sempre due argomenti) deve cambiare.

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

Il modo in cui rispondi a questa domanda potrebbe darti 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 attraversando una qualche forma e dovrebbe essere applicato il modello iteratore.

Se il tuo codice duplicato ha più valori di ritorno (ed è per questo che non puoi fare un semplice metodo di estrazione), allora forse dovresti creare una classe che contiene 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 implementazioni concrete della classe: una per ogni frammento. [Questo è effettivamente il modello di progettazione del metodo modello, da non confondere con il concetto di modelli in C ++. In alternativa, ciò che stai guardando potrebbe essere meglio risolto con il modello di strategia.]

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


4

Quando incontri una situazione come questa, è meglio pensare ad astrazioni "non tradizionali". Forse hai molte duplicazioni all'interno di una funzione e il factoring di una vecchia funzione non si adatta molto bene perché devi passare troppe variabili. Qui, una funzione nidificata in stile D / Python (con accesso all'ambito esterno) funzionerebbe alla grande. (Sì, potresti creare una classe per contenere tutto quello stato, ma se la stai usando solo in due funzioni, questa è una brutta e dettagliata soluzione per non avere funzioni nidificate.) Forse l'eredità non si adatta perfettamente, ma un il mixin avrebbe funzionato bene. Forse quello di cui hai davvero bisogno è una macro. Forse dovresti prendere in considerazione alcuni modelli di metaprogrammazione o riflessione / introspezione o persino programmazione generativa.

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


+1 per molte tecniche eccellenti compresse in spazi ristretti. (Beh, sarebbe stato +1 anche per le tecniche descritte.)
Macneil,

3

Personalmente lo ignoro e vado avanti. È probabile che, se è così strano, è meglio averlo duplicato, potresti passare anni a fare refactoring e il prossimo sviluppatore darà un'occhiata e annullerà il tuo cambiamento!


2

Senza un esempio di codice, è difficile dire perché il tuo codice non abbia astrazioni facilmente identificabili. 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;
  • raggruppare piccoli pezzi in base a tipi di dati comuni o comportamenti astratti;
  • riscrivi il codice duplicato dato i nuovi pezzi;
  • se il nuovo codice sfida ancora una chiara astrazione, suddividilo anche in piccolo e ripeti il ​​processo.

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

Rendi astratto il come dei pezzi di codice più piccoli facendo sì che le loro firme delle funzioni identificino cosa, e i pezzi più grandi dovrebbero essere più facili da classificare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.