2009-11-10 13 views
5

persone che ho sentito consigliano che si dovrebbe sempre utilizzare il Iterator pattern per controllare i cicli piuttosto che un'eccezione (che è come è fatto in Python iterators) o utilizzando il Sentinel pattern, per cui uno speciale, il valore sentinella (spesso null) viene restituito indica la fine dell'iterazione.C'è qualche ragione per evitare il pattern sentinella in Java?

Le migliori pratiche sconsigliano il modello sentinella? Se è così, perché? (a parte il fatto che non funziona con la sintassi foreach in Java 1.5).

Edit: Esempio di codice 1 - Sentinel modello

Reader r = ...; 
for(int val = r.read(); val != -1; val = r.read()) { 
    doSomethingWith(val); 
} 

Esempio di codice 2 - Iterator modello

for(Iterator<Thing> it = getAnIterator() ; it.hasNext();) { 
    Thing t = it.next(); 
    doSomethingWith(t); 
} 
+0

Si potrebbe voler mostrare un esempio di ciascuno dei tre schemi di loop che stai chiedendo. –

+0

Non ha incluso il modo Python, sollevando eccezioni per il controllo del flusso non è qualcosa che sto suggerendo seriamente. –

+0

+1 solo per l'I/O per ciclo - non l'ho mai visto fare in quel modo. –

risposta

15

Il problema con il modello Sentinel è che esclude esplicitamente il valore sentinella dal set di valori che sono elementi validi. Ad esempio, se si dispone di un elenco di oggetti che possono validamente contenere null come elemento, l'utilizzo di null come valore sentinella è un errore.

9

Le eccezioni devono essere utilizzate solo in situazioni "eccezionali". La conclusione o l'interruzione di un ciclo è il normale flusso del programma, non una situazione eccezionale.

Inoltre, quando si lavora in Java (o qualsiasi altra lingua), si desidera utilizzare schemi e convenzioni che sono comuni e ben noti nella comunità, poiché altri programmatori Java potrebbero dover mantenere il proprio codice. Se lo fanno, molto probabilmente si aspettano di vedere gli iteratori, non lo schema sentinella.

+4

Sono d'accordo con questo principio e consiglio di seguirlo. Il segreto è che la versione di lancio delle eccezioni è incredibilmente veloce, specialmente se hai usato un'eccezione nella cache singleton. Ma non lo farei a meno che non avessi bisogno della folle velocità e potrei provare che è stato più veloce. Ne cito qui solo perché è interessante. –

0

Non vedo come il Pattern Sentinel sia abbastanza diverso da essere il proprio schema. Il mio unico pensiero è che sarebbe utile per determinare un valore di stop dallo streaming dei dati. Tuttavia, l'interista si occupa di questo con il requisito del metodo "hasMore".

2

Il pattern Sentinel viene utilizzato nel JDK per la lettura di flussi, quindi non è inaudito, ma è difficile creare un loop. Tutte le soluzioni hanno negativi che un iteratore non ha. La tua soluzione richiede una chiamata ripetitiva da leggere (ovvero la duplicazione del codice). Un ciclo while forza la variabile da dichiarare al di fuori dell'ambito del ciclo. Altre alternative sarebbero sorprendenti e difficili da seguire.

Quindi, alla fine, l'iteratore è il tipo predefinito che deve essere deviato solo per un motivo.

4

Penso che siano entrambi ok ma l'Iterator è più idiomatico in Java (in particolare se in realtà si ha un Iterable su cui è possibile utilizzare il ciclo for-each).

L'unico posto in cui si vede molto la versione di Sentinel in Java è esattamente il caso in cui si è scritto nel codice I/O.

+0

+1 per aver sottolineato che non esiste una regola ferrea che copra tutti i casi, ogni modello ha i suoi pro e contro. – MAK

2

Il mantra è "dì quello che fai e fai quello che dici".

Se si verifica il valore restituito per essere un valore speciale, questo non dice nulla sul motivo per cui lo si verifica. Nel tuo esempio:

for(int val = r.read(); val != -1; val = r.read()) { 
    doSomethingWith(val); 
} 

dice questo "se il valore restituito diventa sempre -1, siamo in grado di saltare il resto", o "se il valore restituito mai è -1, si è verificato un errore", o " se il valore restituito è sempre -1, viene raggiunta la fine "? Al contrario, hasNext è completamente non ambiguo.

A proposito, mi piacciono i costrutti foreach e map che altri linguaggi forniscono (o consentono di scrivere) meglio del ciclo esplicito.

0

"Le eccezioni devono essere utilizzate solo in situazioni" eccezionali "La conclusione o l'interruzione di un ciclo è normale per il flusso del programma, non una situazione eccezionale."

Se un file letto supera un milione di volte e solo l'ultima volta dice "Non riesco a trovare altri record", non lo chiami "eccezionale"?

Non vedo il problema nell'usare, ad esempio, EOFExceptions per il controllo del flusso (se il Sistema è abbastanza sano da risolverli, invece di restituire quello terribilmente stupido -1 intero dicendo che "-1 byte sono stati letti") . Mi dici se il seguente è illeggibile/non modificabile:

try { 
    r = readNext(...); 
    process(r); 
} catch (EOFException e) { 
    ... 
} 
+0

Se non ti piace il Sentinel, un metodo booleano è molto meglio che catturare un'eccezione da un blocco (vero) while. – Yishai

+1

Bene, spero che raggiungere la condizione di uscita "Non riesco a trovare altri record" non è eccezionale. Quando questo è eccezionale, il tuo programma è bacato. Devi separare i casi 'insoliti' e' eccezionali'. Non importa quante letture di file fatte o quant'altro - questo caso non è eccezionale. –

Problemi correlati