Restituzione booleana di set.add () in se condizionale?


15

L'operatore add della classe set restituisce un valore booleano che è vero se l'elemento (che deve essere aggiunto) non era già presente e falso altrimenti. Sta scrivendo

if (set.add(entry)) {
    //do some more stuff
}

considerato un buon stile in termini di scrittura di codice pulito? Mi chiedo da quando fai due cose contemporaneamente. 1) aggiunta dell'elemento e 2) verifica dell'esistenza dell'elemento.


6
Stai parlando dello standard java.util.Set, che ritorna vero addquando l'elemento non era già lì, giusto?
user2357112 supporta Monica

1
Di solito considererei il test opposto:if (!set.add(entry)) {// entry already present, possibly a case you want to handle}
njzk2,

C'è qualcosa di sbagliato nel fare due cose contemporaneamente?
user207421

@ user2357112 sì, esatto.
Andreas Braun,

Risposte:


19

Sì.

Il solito punto in cui un'operazione restituisce un valore booleano è che è possibile utilizzarlo per prendere decisioni, ovvero all'interno di un ifcostrutto. L'unico altro modo in cui potresti realizzare questo potenziale sarebbe quello di memorizzare il valore restituito in una variabile e poi riutilizzarlo immediatamente in un if, che è semplicemente sciocco e sicuramente non è in alcun modo preferibile alla scrittura if(operation()) { ... }. Quindi vai avanti e fallo, non ti giudicheremo (per questo).


Grazie per la tua risposta. Come sottolinea @Niels vanReijmersdal nella sua risposta, ciò interrompe il comando e interroga la separazione, non è vero? Non pensi che sia importante?
Andreas Braun,

4
@AndreasBraun personalmente penso che la separazione delle query di comando sia stupida. Spesso è logico che un metodo esegua un'azione e restituisca un valore correlato a quell'azione. Applicare una separazione artificiale tra i due porta a un codice inutilmente dettagliato.

1
@ dan1111: davvero. Inoltre, la "separazione di comandi e query" porta al "check then act" e ad altri pattern anti, che possono rompersi in contesti multi-thread. È piuttosto strano pubblicizzare una tale separazione, quando il resto del mondo sta cercando di costruire operazioni con fusione atomica per soluzioni SMP robuste ed efficienti allo stesso tempo ...
Holger

2
@ Jörg W Mittag: pagina 759 di cosa? Dal momento che un'operazione atomica è intrinsecamente immune contro l'anti-schema "check then act", non sembra utile dividerlo solo per leggere un "intero capitolo" di qualsiasi cosa scoprire, come rendere sicuro il thread di costruzione ancora. Dal momento che non hai nominato alcun argomento reale, non ho "obiezioni specifiche a tali argomenti".
Holger,

1
@ Jörg W Mittag: Beh, sto parlando della risposta in questa pagina, scoraggiando l'uso Set.addcome singola operazione senza nominare un'alternativa. Se l'operazione prevista è quella di aggiungere un singolo elemento e scoprire se è stato aggiunto, non è necessaria un'alternativa più potente che complica ancora l'operazione senza vantaggi. Spero che tu sappia che Set.addè un metodo JRE incorporato che può essere utilizzato senza prima studiare un libro di> 700 pagine.
Holger,

12

Direi che non è il più pulito possibile, perché costringe il manutentore a sapere o cercare già cosa significa il valore restituito. Significa che il valore già esistito, non esisteva già, è stato inserito correttamente? Se non lo usi molto, non lo saprai, e anche se lo fai, è molto più carico mentale.

Preferirei quanto segue:

boolean added = set.add(entry);

if (added) {
    //do some more stuff
}

Sì, un po 'più dettagliato, ma il compilatore dovrebbe generare praticamente lo stesso identico bytecode e anche le persone che non usano i set Java da anni possono seguire la logica senza cercare nulla.



9
Cercare un metodo è meno oneroso che cercare di comprendere l'intenzione dello sviluppatore originale per una variabile ridondante dichiarata al di fuori dell'ambito in cui viene utilizzata. In tutti gli IDE Java moderni, uno sviluppatore può passare il puntatore del mouse su un metodo e accedere immediatamente alla sua documentazione. I bravi sviluppatori lo fanno costantemente quando lavorano con qualsiasi API sconosciuta.
Kevin Krumwiede,

9
Io secondo @Holger. Se non lo sai Set.add, sei inesperto e dovresti cercare questi metodi per impararli e diventare più esperti.
Ripristina Monica il

7
notAlreadyPresentnon è la migliore espressione. Userei addede mi aspetto che il lettore sappia perché un valore non dovrebbe essere aggiunto a un set.
njzk2,

3
@JustinTime: l'utilizzo dei commenti per descrivere ciò che fa il codice è considerato una cattiva pratica. Il tuo codice dovrebbe essere auto-descrittivo. In una recensione di codice, contrassegnerei il tuo esempio come inaccettabile.
BlueRaja - Danny Pflughoeft

8

Se vero significa successo, allora è un codice buono e chiaro.

Esiste una convenzione diffusa secondo cui una funzione o un metodo restituisce true (o qualcosa che valuta true) in caso di successo. Finché il tuo codice lo segue, penso che mettere il metodo nel condizionale vada bene.

Codice come questo è inutilmente ingombra a mio avviso:

boolean frobulate_succeeded = thing.frobulate();

if (frobulate_succeeded) {
    ...
}

Sembra che tu ti stia ripetendo.

Tuttavia, la domanda è ambigua sul significato del valore restituito. Dici "un valore booleano che indica se l'elemento aggiunto esisteva già", il che potrebbe implicare che vero significhi che l'elemento esiste (e che l'aggiunta non si è verificata). In tal caso, idealmente cambierei il comportamento di ritorno del metodo per renderlo più convenzionale. Se ciò non fosse possibile, aggiungerei una variabile intermedia aggiuntiva che ti consente di etichettare chiaramente il risultato di ritorno nel tuo codice (come suggerito da altri).


Cosa significa successo nel contesto dell'aggiunta di un elemento a un set? Non gettare un'eccezione significa successo; true vs. false significa qualcosa di più specifico in questo contesto.
Ripristina Monica il

@ Solomonoff'sSecret In questo caso, un'eccezione indica che l'insieme si è rifiutato di aggiungere l'elemento, falsesignifica che l'elemento non è stato aggiunto perché era già contenuto e trueindica che l'elemento non era già contenuto. Poiché add()aggiunge l'elemento all'insieme se l'insieme non lo conteneva già, truesignifica quindi che l'elemento è stato aggiunto correttamente all'insieme.
Justin Time - Ripristina Monica il

@JustinTime Stai aggiungendo i tuoi giudizi di valore ai due casi. Un'altra interpretazione è che il successo è che l'elemento è nell'insieme quando il metodo ritorna. Se l'oggetto era già nel set, ha addesito positivo senza dover fare nulla. Se l'oggetto non era già nel set, addriesce aggiungendo l'articolo al set. L'interpretazione corretta è arbitraria. La definizione meno arbitraria di successo è quella della lingua: il metodo ha successo se ritorna normalmente.
Ripristina Monica il

1
La definizione meno arbitraria di successo è che il metodo ha eseguito correttamente l'attività che si prefiggeva di eseguire. Se ho un metodo firstLessThanSecond(int l, int r), e restituisce truese l > ro falsese l <= r, allora quel metodo non ha successo, anche se restituisce normalmente.
Justin Time - Ripristina Monica il

1
@JustinTime addè una grave abbreviazione del contratto. La documentazione inizia con "Aggiunge l'elemento specificato a questo set se non è già presente", che è soddisfatto se l'articolo era già nel set. Sei libero di interpretare il valore di ritorno come desideri, ma alla fine è solo la tua interpretazione. E nel tuo secondo esempio il metodo valuta se una condizione è vera. Se la condizione è falsa, il metodo certamente non ha fallito, poiché ha valutato con successo il condizionale.
Ripristina Monica il

2

Direi che è molto simile a C. Il più delle volte preferirei avere una variabile con nome descrittivo per un risultato di mutazione e nessuna mutazione si verifica in una ifcondizione.

Un compilatore eliminerà questa variabile se viene immediatamente riutilizzata. Un essere umano avrà più tempo a leggere la fonte; per me è più importante.

Se qualcuno dovesse estendere la condizione aggiungendo una clausola and/ oralla condizione if, potrebbe finire per non chiamare .add()in certi casi a causa della valutazione del corto circuito. A meno che il cortocircuito non sia specificamente previsto, questo può finire come un bug.


Se scrivo if (set.contains(entry)){set.add(entry); //do more stuff}viene eliminato anche dal compilatore?
Andreas Braun,

1
@AndreasBraun: io non la penso così; il compilatore non ha idea del legame semantico tra containse add. Inoltre, raddoppia il lavoro di ricerca entrynel set; questo può avere un ruolo per set molto grandi e loop molto leggeri.
9000,

1
Quindi penso che preferiresti non scrivere, if (set.contains(entry)){set.add(entry); //do more stuff}ma preferirei ad esempio la risposta di Karl Bielefeldt?
Andreas Braun,

@AndreasBraun: esattamente (votato per quella risposta).
9000,

1
un buon punto. Le mutazioni negli if sono difficili, poiché un futuro sviluppatore potrebbe avere altre condizioni con cortocircuiti.
njzk2,

0

Il codice sembra interrompere la separazione delle query di comando . Questo è discusso nel libro Clean Code e nel video Struttura delle funzioni . Quindi dal punto di vista del codice pulito penso che non sia considerato un buon stile.

“Il codice pulito è semplice e diretto. Il codice pulito si legge come una prosa ben scritta. Il codice pulito non oscura mai l'intento del designer, ma è piuttosto pieno di astrazioni nitide e linee di controllo semplici.

- Grady Booch autore di Object Oriented Analysis and Design with Applications ”

Per me l'intento del tuo codice non è chiaro. Se viene eseguita entrambe le operazioni quando la voce viene aggiunta correttamente o anche quando esiste già? Cosa add()ritorna? l'oggetto? Il codice di errore?


2
Beh, sì, è quello che ho pensato anche io. Ma penso anche che @Kilian Foth abbia ragione. Se volessi separare query e comandi, dovrei scrivere if (set.contains(entry)){set.add(entry); //do more stuff}che sembra un po 'sciocco. Cosa ne pensi di questo?
Andreas Braun,

Non conosco il dominio e il contesto di questo codice per suggerire un buon nome, ma lo avvolgo in una funzione con un chiaro intento. Ad esempio: doesEntryExistOrCreateEntry (entry), questo potrebbe contenere la tua contiene () + add () logica e restituire un valore booleano. Domanda simile qui: softwareengineering.stackexchange.com/questions/149708/… che discute questa pratica al di fuori del codice pulito.
Niels van Reijmersdal,

6
Penso che la regola di separazione delle query di comando sia stupida, in particolare se applicata a un caso come questo, in cui un metodo esegue un'azione e restituisce lo stato di successo dell'azione. Inoltre, non spieghi quale sia la regola o fornisci molto in termini di giustificazione. Sottovalutato per questi motivi.

1
"Cosa restituisce add ()?" Mi aspetto che qualsiasi sviluppatore Java che esegua la revisione del codice conosca l' CollectionAPI.
njzk2,

3
Concordato con @ dan1111. È particolarmente stupido perché è il genere di cose che crea terreno fertile per le condizioni di gara.
Paul Draper,
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.