Duplicazione di codice illusoria

54

Il solito istinto è rimuovere qualsiasi duplicazione di codice che vedi nel codice. Tuttavia, mi sono trovato in una situazione in cui la duplicazione è illusoria .

Per descrivere la situazione in maggiore dettaglio: sto sviluppando un'applicazione web e la maggior parte delle visualizzazioni sono fondamentalmente le stesse: visualizzano un elenco di elementi che l'utente può scorrere e scegliere, un secondo elenco che contiene elementi selezionati e un pulsante "Salva" per salvare il nuovo elenco.

Mi è sembrato che il problema fosse facile. Tuttavia, ogni vista ha le sue peculiarità: a volte è necessario ricalcolare qualcosa, a volte è necessario memorizzare alcuni dati aggiuntivi, ecc. Questi, ho risolto inserendo i hook di callback nel codice logico principale.

Ci sono così tante minime differenze tra le visualizzazioni che sta diventando sempre meno manutenibile, perché ho bisogno di fornire callback per praticamente tutte le funzionalità, e la logica principale inizia ad assomigliare ad una sequenza enorme di invocazioni di richiamata. Alla fine non sto risparmiando tempo o codice, perché ogni vista ha il suo codice che viene eseguito, tutto in callback.

I problemi sono:

  • le differenze sono così minute che il codice sembra quasi esattamente simile in tutte le visualizzazioni,
  • ci sono tante differenze che quando si guardano i dettagli, il codice è non un po 'simile

Come dovrei gestire questa situazione?
La logica di base composta interamente da callback call è una buona soluzione?
O dovrei piuttosto duplicare il codice e eliminare la complessità del codice basato su callback?

    
posta Mael 16.10.2015 - 13:15
fonte

6 risposte

52

In fin dei conti devi una sentenza se combinare un codice simile per eliminare la duplicazione.

Sembra esserci una sfortunata tendenza a prendere principi come "Non ripeterti" come regole che devono essere seguite a memoria in ogni momento. In realtà, queste non sono regole universali, ma linee guida che dovrebbero aiutarti a pensare e sviluppare un buon design.

Come tutto nella vita, devi considerare i benefici rispetto ai costi. Quanto codice duplicato sarà rimosso? Quante volte è stato ripetuto il codice? Quanto ci vorrà per scrivere un design più generico? Quanto potresti sviluppare il codice in futuro? E così via.

Senza conoscere il tuo codice specifico, questo non è chiaro. Forse c'è un modo più elegante per rimuovere la duplicazione (come quella suggerita da LindaJeanne). Oppure, forse semplicemente non c'è abbastanza ripetizione vera per giustificare l'astrazione.

L'attenzione insufficiente al design è una trappola, ma attenzione anche alla progettazione eccessiva.

    
risposta data 16.10.2015 - 13:57
fonte
42

Ricorda che DRY riguarda la conoscenza . Non importa se due parti di codice sono simili, identiche o totalmente diverse, ciò che conta è se lo stesso pezzo di conoscenza sul tuo sistema sia presente in entrambi.

Un pezzo di conoscenza potrebbe essere un fatto ("la deviazione massima consentita dal valore previsto è 0,1%") o potrebbe essere un aspetto del processo ("questa coda non contiene mai più di tre elementi"). È essenzialmente ogni singola informazione codificata nel codice sorgente.

Quindi, quando decidi se qualcosa è duplicazione da rimuovere, chiedi se si tratta di duplicazione della conoscenza. In caso contrario, è probabilmente una duplicazione accidentale e l'estrazione in un posto comune causerà problemi quando in seguito si desidera creare un componente simile in cui quella parte apparentemente duplicata è diversa.

    
risposta data 16.10.2015 - 17:50
fonte
27

Hai considerato l'utilizzo di un modello di strategia ? Avresti una classe View che contiene il codice comune & routine chiamate da più viste. I bambini della classe View contengono il codice specifico per tali istanze. Userebbero tutti l'interfaccia comune che hai creato per la vista, e quindi le differenze sarebbero incapsulate e amp; coerente.

    
risposta data 16.10.2015 - 13:41
fonte
5

Qual è il potenziale per il cambiamento? Ad esempio, la nostra applicazione ha 8 diverse aree di business con un potenziale di 4 o più tipi di utenti per ciascuna area. Le viste sono personalizzate in base al tipo di utente e all'area.

Inizialmente, questo è stato fatto usando la stessa vista con alcuni controlli qua e là per determinare se dovessero mostrare cose diverse. Nel corso del tempo, alcune delle aree aziendali hanno deciso di fare cose drasticamente diverse. Alla fine, abbiamo sostanzialmente migrato a una vista (viste parziali, nel caso di ASP.NET MVC) per porzione di funzionalità per area di business. Non tutte le aree di business hanno la stessa funzionalità, ma se si desidera la funzionalità di un'altra, quell'area ottiene la propria vista. È molto meno ingombrante per la comprensione del codice e per la testabilità. Ad esempio, apportare una modifica per un'area non causerà modifiche indesiderate per un'altra area.

Come menzionato @ dan1111, può venire giù per una chiamata di giudizio. Col tempo, potresti scoprire se funziona o meno.

    
risposta data 17.10.2015 - 00:14
fonte
2

Un problema potrebbe essere che stai fornendo un'interfaccia (interfaccia teorica, non funzionalità linguistica) a un solo livello della funzionalità:

A(a,b,c) //a,b,c are your callbacks or other dependencies

Invece di più livelli a seconda della quantità di controllo richiesta:

//high level
A(a,b,c)
//lower
A myA(a,b)
B(myA,c)
//even lower
A myA(a)
B myB(myA,b)
C myC(myB,c)
//all the way down to you just having to write the code yourself

Per quanto ho capito, esponi solo l'interfaccia di alto livello (A), nascondendo i dettagli di implementazione (le altre cose lì).

Nascondere i dettagli di implementazione ha dei vantaggi e hai appena trovato uno svantaggio: il controllo è limitato, a meno che tu non aggiunga esplicitamente funzionalità per ogni singola cosa che sarebbe stata possibile quando si utilizza direttamente l'interfaccia di basso livello.

Quindi hai due opzioni. O usi solo l'interfaccia di basso livello, usa l'interfaccia di basso livello perché l'interfaccia di alto livello è troppo lavoro da mantenere, o espone interfacce sia di alto che di basso livello. L'unica opzione sensata è quella di offrire sia interfacce di livello alto che di livello basso (e tutto ciò che si trova tra di esse), supponendo che si desideri evitare il codice ridondante.

Poi, quando scrivi un altro dei tuoi aspetti, guardi tutte le funzionalità disponibili che hai scritto fino a quel momento (innumerevoli possibilità, fino a te per decidere quali potrebbero essere riutilizzate) e unirle insieme.

Utilizza un singolo oggetto in cui hai bisogno di un controllo ridotto.

Utilizza la funzionalità di livello più basso quando è necessario che si verifichi qualche stranezza.

Anche non molto in bianco e nero. Forse la tua grande classe di alto livello può ragionevolmente coprire tutti i possibili casi d'uso. Forse i casi d'uso sono così variabili che basta solo la funzionalità primitiva di livello più basso. Fino a te per trovare il saldo.

    
risposta data 17.10.2015 - 22:44
fonte
1

Ci sono già altre risposte utili. Aggiungerò il mio.

La duplicazione è negativa perché

  1. ingombra il codice
  2. ingombra la nostra adesione al codice ma la cosa più importante
  3. perché se cambi qualcosa qui e anche tu devi cambiare qualcosa , potresti dimenticarlo / introdurlo bug / .... ed è difficile non dimenticarlo mai.

Quindi il punto è: non stai eliminando la duplicazione per il gusto o perché qualcuno ha detto che è importante. Lo stai facendo perché vuoi ridurre bug / problemi. Nel tuo caso, sembra che se modifichi qualcosa in una vista, probabilmente non sarà necessario modificare la stessa riga in tutte le altre visualizzazioni. Quindi hai apparente duplicazione , non una vera e propria duplicazione.

Un altro punto importante è non riscrivere mai da zero qualcosa che funziona ora solo sulla base di una questione di principio, come diceva Joel (avresti potuto già sentire parlare di lui ...). Quindi, se le tue opinioni stanno funzionando, procedi a migliorare passo dopo passo e non cadere preda del "singolo peggior errore strategico che qualsiasi azienda di software possa fare".

    
risposta data 20.10.2015 - 14:40
fonte

Leggi altre domande sui tag