È copia e incolla la testata di for-loop (ad es. :(let i = 0; isomething.length; i ++)) che viola il principio DRY?

3

Ad esempio, nel mio progetto, ho trovato spesso che alcune parti del ciclo appaiono molte volte, ad esempio:

for(let i=0;i<SharedData.students.length;i++){
    SharedData.students[i].something=.....
}

if(isReset){
    for(let i=0;i<SharedData.students.length;i++){
        SharedData.students[i].reset();
    }
}
.
.
.

che l'attività all'interno e all'esterno del ciclo è completamente diversa, ma di solito ha bisogno di

for(let i=0;i<SharedData.students.length;i++)

. Quindi la mia domanda è, copiare e incollare

for(let i=0;i<SharedData.students.length;i++)

violare il principio DRY?

    
posta mmmaaa 14.12.2018 - 03:51
fonte

5 risposte

20

Il principio "Non ripetere te stesso" (DRY) è facile da applicare senza mente.

Ricorda che il vero peccato non è l'uso di copia e incolla. Sta diffondendo una decisione in un modo che rende difficile cambiare quella decisione. Se quello che hai veramente sono due decisioni che sembrano tutte uguali al momento, allora va tutto bene. Faresti dei danni se costringessi a esprimere le due decisioni nello stesso posto.

Lasciandoli separati, come hai ora, stai permettendo ai due anelli di variare in modo indipendente. Se li hai riscritti come suggerisce Robert Harvey:

for(let i=0;i<SharedData.students.length;i++){
    SharedData.students[i].something=.....
    if(isReset){
        SharedData.students[i].reset();
    }
}

quindi potresti perdere la capacità di farli facilmente variare in modo indipendente (ad esempio, se si salta il primo elemento, per qualsiasi motivo).

Questa idea può essere difficile da comprendere, quindi lasciami dire in un altro modo:

int x = 100;
int y = 100;    

Ecco una "violazione" di DRY a cui la maggior parte delle persone non penserebbe due volte. Perché? Perché sappiamo che anche se y è una copia ridondante di x, potrebbe non essere sempre. Ha il suo significato. Non vogliamo perdere questo significato solo perché in questo momento ha lo stesso valore di x.

Quindi, per favore, quando pensi a DRY, pensa meno a copia e incolla e più a ciò che stai rendendo facile cambiare.

    
risposta data 14.12.2018 - 05:03
fonte
7

Is this violating the DRY principle?

In un certo senso, si - e penso che sia un po 'sorprendente che alcuni commentatori qui sembrano ignorarlo o negarlo. In effetti, le conseguenze sono spesso accettabili in molti casi reali, ma penso che valga la pena dare un'occhiata più da vicino all'esempio.

Quindi supponiamo per un momento questa affermazione

for(let i=0;i<SharedData.students.length;i++){
    SharedData.students[i].something=.....

appare 100 volte in un programma. Poi ci sono alcune decisioni di design che sono già diventate difficili da cambiare:

  1. la decisione di avere SharedData un attributo chiamato students

  2. la decisione che students è una matrice indicizzabile

  3. la decisione che gli studenti abbiano un campo mutabile chiamato something

(Naturalmente, non hai scritto per ripetere la parte interna del ciclo, ma lascia che ti metta questo esempio a scopo di dimostrazione)

Quindi, come puoi mitigare questi problemi? Il primo può essere mitigato evitando di ripetere l'espressione esplicita SharedData.students più spesso del necessario. Spesso, una semplice variabile locale aggiuntiva può aiutare:

 let studentArray = SharedData.students;
 for(let i=0;i<studentArray.length;i++){
        studentArray.something=.....

Si noti che questo semplice cambiamento divide da solo il numero di ripetizioni di SharedData.students per due. Su una scala più ampia, potresti considerare di avere diverse funzioni implementate in termini di parametro studentArray invece di un parametro SharedData .

Il numero 2 può essere mitigato, ad esempio, utilizzando un'istruzione foreach , se il tuo linguaggio di programmazione ha una cosa del genere:

  foreach(student in studentArray){
       student.something = ...

Ora è solo necessario avere students di un contenitore iterabile, che è un'ipotesi più debole rispetto a un array indicizzabile.

Il numero 3 può essere attaccato incapsulando la parte interna del ciclo for all'interno di una funzione:

foreach(student in studentArray)
      DoSomething(student);

Ora, la logica di manipolare o usare student in un modo specifico è in un posto, non più 100.

Forse vale anche la pena di dare un'occhiata al motivo per cui una tale for-head si ripete così spesso all'interno di un programma. Potrebbe essere un segnale che la sezione di codice generale contenente il ciclo for può essere generalizzata, magari introducendo l'operazione come parametro stesso (preferisco la sintassi di C #, immagino tu abbia l'idea):

 void DoSomethingForAllStudents(Action<Student> DoSomething)
 {
     foreach(student in studentArray)
         DoSomething(student);
 }

Ma attenzione, questo può già essere sovrastimato, e se il costo di rendere le cose meno ESSENZIALI è eccessivamente complicato, è meglio lasciare le cose come sono.

Come ho scritto all'inizio, in molti casi reali i problemi citati sono decisioni di progettazione che non cambierai più avanti durante l'intera vita del tuo programma, o dove il numero reale di ripetizioni non è così elevato . Quindi, anche se questo letteralmente violando DRY, non pensarci troppo.

    
risposta data 14.12.2018 - 07:55
fonte
1

Sì, questo sta violando il principio DRY. Il principio DRY non è tuttavia assoluto e dovrebbe essere usato con uno standard personale. Non viviamo in un mondo perfetto.

Tuttavia, la frequenza di utilizzo è molto importante. Se si copia e incolla lo stesso codice frequentemente, direi che dovresti ESSERE ASCIUTTO.

    
risposta data 14.12.2018 - 23:46
fonte
0

Prima un altro, più riconoscibile, esempio di ripetizioni per loop:

for (let i = 0; i < columnCount; ++i) {
    write getColumnTitle(i);
}
writeln();
for (let rowI = 0; rowI < rowCount; ++rowI) {
    row = rows.get(i);
    for (let i = 0; i < columnCount; ++i) {
        write row.getColumnData(i);
    }
    writeln();
}

È facile essere d'accordo, che il ciclo sia un po 'prolisso (cioè il i ), un iteratore, o meglio un flusso (che passa ripetutamente un'espressione di funzione lambda chiamata più volte) potrebbe essere migliore.

Ma in questo caso il principio DRY non regge.

Tuttavia, se vedi il seguente schema:

for i A
B
for i C
D
for i E
F

Quindi il problema non è ASCIUTTO ma piuttosto che il codice funziona generalmente su intere i-tuple, record, ma il ciclo gestisce una seconda dimensione.

rec.a()
B
rec.c()
D
rec.e()
F

Questo codice sembra un'elaborazione sequenziale ed è quasi un ciclo annidato for j for i X Y .

L'errore più probabile qui è una violazione Separazione delle preoccupazioni .

Ad esempio l'importazione da un file Excel in un database ha anche questo approccio bidimensionale: leggere Excel, scrivere i record del database.

La best practice è:

  • crea un lettore di tabelle Excel, che può essere passato:
    • una definizione di tabella
    • una funzione di importazione di righe
  • crea uno scrittore di database di righe
  • rendere l'importatore integratore

Sinossi

  1. L'approccio rec.a () di eliminare i cicli for, è un passo avanti, ma potrebbe causare troppa complessità di molti metodi molto specifici e altamente legati.

  2. La separazione delle preoccupazioni potrebbe semplificare le cose: anche il suddetto lettore di Excel potrebbe essere sviluppato in modalità test-driven in un test unitario.

risposta data 14.12.2018 - 15:47
fonte
0

Dipende.

Lo scopo di DRY principal non è tanto eliminare linee di codice "dall'aspetto identico" quanto "facilitare la coerenza" e "ridurre l'onere del cambiamento".

Prendendo il tuo esempio,

for(let i=0;i<SharedData.students.length;i++)

In quali circostanze questa linea cambierebbe? Queste circostanze sono "probabili?" E, nel caso di quel cambiamento, anche le linee identiche o simili cambiano identicamente ??

Usa il tuo giudizio. Calibrare le proprie sensibilità nel tempo.

    
risposta data 15.12.2018 - 02:25
fonte

Leggi altre domande sui tag