È "continua" e "interrompe" nei loop antipattern / cattiva pratica in Java? [duplicare]

1
    

Questa domanda ha già una risposta qui:

    

Vedo lo scopo principale di continue nella programmazione, può farti uscire dal resto dei passi del ciclo come:

while(condition1){
  ... code ...
  if(!condition2){
    continue;
  }
  ... code ...
  if(condition3){
    break;
  }
  ... code ...
}

Capisco che condizione2 e condizione3 possano dipendere dal codice interno, quindi è utile creare una via di fuga separata dalla fase del ciclo. Ma significa che il codice dovrebbe essere refactored invece di costruire vie di fuga, come ripensare la condizione originale1?

Direi se ho bisogno di diverse parti continue e break , mi dispiace per il mio codice, di solito sto lavorando senza continue e non mi manca davvero.

Un altro uso di continue e break che posso accettare è l'ottimizzazione, ma ho ancora la stessa sensazione al riguardo, come ho detto sopra. Per alcuni motivi ho sentito "usare GOTO". Ho ragione, se non lo sono, perché?

    
posta CsBalazsHungary 22.07.2014 - 14:01
fonte

2 risposte

5

Correzione: continue ti fa uscire da una iterazione di un ciclo, non dall'intero ciclo.

Saltare il resto di un'iterazione di un ciclo è come tornare presto da una chiamata di metodo (in effetti, molti corpi lunghi dovrebbero essere chiamate di metodo). Quindi valgono gli stessi argomenti validi per " return early o use nested if ?", Che è già una polemica controversa e ben nota; Dubito che raggiungerai un consenso anche su questa domanda.

La mia posizione è: continue è una parola chiave per un motivo; non è in alcun modo automaticamente negativo nel modo in cui la maggior parte delle persone pensa che goto sia cattivo.

    
risposta data 22.07.2014 - 14:04
fonte
4

Il classico goto ha forme diverse con varie restrizioni su di esse. Be it return , try{} catch{} , throw , break o continue . Sono tutti veramente goto con alcuni bit aggiuntivi attorno ad esso.

Uno potrebbe refactificare continue o break in un altro stile di loop con return:

for (int i = 0; i <= max; i++) {
  if(someTest) { continue; }
  if(someOtherTest) { continue; }
  doStuff;
}

Può essere scritto come:

for (int i = 0; i <= max; i++) {
  func();
}
....

void func() {
  if(someTest) { return; }
  if(someOtherTest) { return; }
  doStuff;
}

Quale è più facile da leggere? Bene, io tenderei a sostenere che il primo è perché il secondo perde il contesto del ciclo ... ma sono altrimenti uguali. È giusto che le persone tendano a vedere meno di continue di quanto facciano con return nel codice quotidiano.

Da Esercitazioni Java: dichiarazioni di diramazione break e continue quando utilizzato le etichette hanno un po 'più di energia di quanto la maggior parte delle persone capisca. Rende anche il codice un po 'più esplicito per quanto riguarda il quale loop viene interpretato dall'istruzione di controllo. Prendere in considerazione:

class ContinueWithLabelDemo {
    public static void main(String[] args) {
        String searchMe = "Look for a substring in me";
        String substring = "sub";
        boolean foundIt = false;

        int max = searchMe.length() -  substring.length();

    test:
        for (int i = 0; i <= max; i++) {
            int n = substring.length();
            int j = i;
            int k = 0;
            while (n-- != 0) {
                if (searchMe.charAt(j++) != substring.charAt(k++)) {
                    continue test;
                }
            }
            foundIt = true;
            break test;
        }
        System.out.println(foundIt ? "Found it" : "Didn't find it");
    }
}

Si noti che continue e break vengono utilizzati con un'etichetta che specifica il ciclo su cui continuano o interrompono (l'etichetta su break non è necessaria perché è un solo livello, ma aiuta nella comprensione del codice).

Se lo volessi davvero, potresti rifattare questo codice in:

public class Demo {
    public static void main(String[] args) {
        String searchMe = "Look for a substring in me";
        String substring = "sub";
        boolean foundIt;

        int max = searchMe.length() - substring.length();

        foundIt = loop2(searchMe, substring, max);
        System.out.println(foundIt ? "Found it" : "Didn't find it");
    }

    private static boolean loop2(String searchMe, String substring, int max) {
        for (int i = 0; i <= max; i++) {
            if (loop(searchMe, substring, i)) {
                return true;
            }
        }
        return false;
    }

    private static boolean loop(String searchMe, String substring, int j) {
        int n = substring.length();
        int k = 0;
        while (n-- != 0) {
            if (searchMe.charAt(j++) != substring.charAt(k++)) {
                return false;
            }
        }
        return true;
    }
}

( Penso di aver fatto quel refactoring anche se ci sono sicuramente dei bit da brontolare su ) ... ma non sono proprio sicuro di cosa ti guadagna . E questi due metodi con rendimenti multipli attireranno l'ira dei "ritorni multipli sono una folla anti-pattern" (che hanno punti validi come continue e break sono folla anti-pattern - e nel caso precedente probabilmente anche l'ira più giustificata). Sì, questo potrebbe essere di nuovo scritto in un modo che fa uso di altri test che renderanno la freccia " un anti-pattern "la gente ti guarda divertente.

continue è un'istruzione guardia in un ciclo. break è un ritorno anticipato.

Perché qualcosa non viene usato molto non lo rende un modello anti. Significa solo che le persone non sono così familiari con esso.

Per inciso, goto è che vede un po 'di ritorno in C # dove può essere utilizzato con restrizioni (nota che C # non ha un break etichettato come fa Java e quindi senza passare attraverso i cerchi dei metodi di estrazione, questo è il modo di scrivere il codice).

    
risposta data 22.07.2014 - 16:04
fonte

Leggi altre domande sui tag