Come nominare un metodo che esegue sia un'attività che restituisce un valore booleano come stato?


33

Se esiste un metodo

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

dovrebbe piuttosto essere chiamato IsStuffDone()?

Entrambi i nomi potrebbero essere interpretati erroneamente dall'utente: se il nome è DoStuff()perché restituisce un valore booleano? Se il nome è, IsStuffDone()non è chiaro se il metodo esegue un'attività o ne controlla solo il risultato.

C'è una convenzione per questo caso? O un approccio alternativo, poiché questo è considerato difettoso? Ad esempio, nelle lingue che hanno parametri di output, come C #, una variabile di stato booleana potrebbe essere passata al metodo come una e il tipo di ritorno del metodo sarebbe void.

EDIT: Nel mio particolare problema, la gestione delle eccezioni non può essere delegata direttamente al chiamante, poiché il metodo fa parte di un'implementazione dell'interfaccia. Pertanto, il chiamante non può essere incaricato di gestire tutte le eccezioni di diverse implementazioni. Non ha familiarità con queste eccezioni. Tuttavia, il chiamante può gestire un'eccezione personalizzata StuffHasNotBeenDoneForSomeReasonExceptioncome è stato suggerito nella risposta e nel commento di npinti .


2
Dipende dall'uso della funzione e dalle cose che si stanno facendo. Se è necessario, che le cose debbano essere eseguite correttamente, considererei questo approccio imperfetto, perché l'utente della funzione potrebbe perdere il flag booleano e anche le informazioni fornite dall'eccezione.
Benni,

7
Il più delle volte, lo definirei "rotto" in quanto restituire un booleaninvece di avvolgere o passare l'eccezione è quasi sempre sbagliato.
maaartinus,

2
Questo tipo di gestore delle eccezioni è raramente una buona idea. Prendi solo le eccezioni specifiche che ti aspetti, non tutte. E se possibile, evita di lanciarlo in primo luogo, eliminando la necessità di prenderlo.
CodesInChaos,

2
Umm ... BadlyDesignedMethodInSeriousNeedOfRefactoring? E per rispondere alla tua domanda sulle eccezioni: o lascerei che il chiamante li gestisca, o li catturi e poi lanci un'eccezione personalizzata che significa "questo metodo non fa il suo lavoro". Condividi e divertiti.
Bob Jarvis - Ripristina Monica l'

5
A tutti coloro che stanno dicendo: basta lanciare (o lasciare passare) un'eccezione, si stanno facendo ipotesi infondate su come viene utilizzato questo codice. Uno scenario possibile è che c'è un problema da risolvere, con vari metodi di soluzione euristica che risolvono sottoclassi sempre più grandi del problema a costi sempre crescenti; avrebbe senso scrivere qualcosa del genere if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. Fare lo stesso con le eccezioni (personalizzate?) Sarebbe inutilmente più complicato.
Marc van Leeuwen,

Risposte:


68

In .NET, spesso si hanno coppie di metodi in cui uno di essi potrebbe generare un'eccezione ( DoStuff) e l'altro restituisce uno stato booleano e, in caso di esecuzione corretta, il risultato effettivo tramite un parametro out ( TryDoStuff).

(Microsoft lo chiama "Try-Parse Pattern" , poiché forse l'esempio più importante per questo sono i TryParsemetodi di vari tipi primitivi.)

Se il Tryprefisso non è comune nella tua lingua, probabilmente non dovresti usarlo.


3
+1 Ecco come ho visto questo genere di cose fatto altrove. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");è un linguaggio piuttosto standard e intuitivo secondo me.
Karl Nicoll,

12
Va notato che TryDoStuffsi presume che i metodi falliscano in modo pulito e non abbiano effetti collaterali quando restituiscono false.
Trillian,

Cordiali saluti, ci sono anche Removemetodi per esempio nel framework .NET che rimuoverà un elemento da una struttura di dati (ad esempio Dictionary) e restituirà a bool. Il consumatore dell'API può decidere di consumarlo o ignorarlo. Tuttavia, gli errori vengono segnalati come eccezioni.
Omer Iqbal,

18

Cosa succede se si lancia semplicemente l'eccezione al codice chiamante?

In questo modo stai delegando la gestione delle eccezioni a chi sta usando il tuo codice. Che cosa succede se, in futuro, si desidera effettuare le seguenti operazioni:

  • Se non vengono generate eccezioni, agire A
  • Se (ad esempio) FileNotFoundExceptionviene lanciato a, agire B
  • Se viene generata un'altra eccezione, agire C

Se si respinge l'eccezione, la modifica di cui sopra implicherebbe semplicemente l'aggiunta di un catchblocco aggiuntivo . Se lo lasci così com'è, dovrai cambiare il metodo e il luogo da cui viene chiamato il metodo, che, a seconda della complessità del progetto, può trovarsi in più posizioni.


1
Che cosa succede se l'eccezione è di tipo che non può essere delegata e deve essere trattata internamente?
Limbo Exile,

2
@LimboExile: penso che potresti ancora occupartene internamente e poi, magari lanciare un'altra eccezione, forse la tua. Ciò illustrerebbe che si è verificato qualcosa che non avrebbe dovuto succedere, ma allo stesso tempo non hai esposto ciò che sta realmente succedendo, il che presumo sia il motivo per cui vuoi affrontare l'eccezione internamente.
npinti,

6
Vale forse la pena ricordare che il lancio di un'eccezione dovrebbe essere un caso eccezionale . Se è normale o normale DoStuffche fallisca, lanciare un'eccezione per il caso di fallimento sarebbe come controllare il flusso del programma con eccezioni che non vanno bene. Le eccezioni hanno anche un costo inerente alle prestazioni. Se DoStufffallire è un caso insolito a causa di un errore, allora le eccezioni sono sicuramente la strada da percorrere come suggerisce @npinti.
Karl Nicoll,

1
@KarlNicoll Se qualcosa è "eccezionale" è completamente soggettivo. I criteri principali dovrebbero essere se si desidera che il programma si arresti in modo anomalo se nessuno fa qualcosa riguardo all'errore e se si desidera che la possibilità di errore sia presente nel tipo di funzione. Inoltre, non è un fatto che le eccezioni siano costose; dipende dalla lingua e da come li lanci. Le eccezioni sono economiche in Python e se si rimuovono le informazioni di traccia dello stack possono essere economiche anche in Java. Anche se ci fosse un costo prestazionale, è l'ottimizzazione prematura a preoccuparsene fino a quando non hai profilato.
Doval,

1
@Doval - Sono d'accordo che è soggettivo, motivo per cui OP non dovrebbe scartare del tutto la risposta di Npinti, in quanto potrebbe essere il miglior modo di agire. Il mio punto era che lanciare eccezioni non è sempre la strada migliore da prendere. Ci sono molti casi in cui un errore non giustifica il lancio di un'eccezione e il potenziale arresto anomalo di un'applicazione. Ad esempio, se il DoStuff()metodo OP viene ripulito dopo che il codice interno genera un'eccezione e le condizioni Post del metodo sono ancora corrette, un codice di ritorno potrebbe essere un'opzione migliore poiché l'errore è stato gestito.
Karl Nicoll,

7

DoStuff() è sufficiente e il valore di ritorno della funzione deve essere documentato e non è necessario menzionarlo nel nome della funzione, cercando molte API disponibili:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-Sharp

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )

6

In Java, l' API Collection definisce un metodo add che restituisce un valore booleano. Fondamentalmente restituisce se l'aggiunta ha modificato la raccolta. Quindi per un di Listsolito tornerà vero, dal momento che l'oggetto è stato aggiunto, mentre per un Setpotrebbe restituire falso, se l'articolo era già lì, poiché Setconsente ogni elemento unico al massimo una volta.

Detto questo, se aggiungi un elemento non consentito dalla raccolta, ad esempio un valore null, l'aggiunta genererà un valore NullPointerExceptionanziché restituire false.

Quando applichi la stessa logica al tuo caso, perché devi restituire un valore booleano? Se è per nascondere un'eccezione, non farlo. Basta lanciare l'eccezione. In questo modo puoi vedere cosa è andato storto. Se hai bisogno di un valore booleano, perché potrebbe non essere stato fatto, per qualche motivo diverso da un'eccezione, dai un nome al metodo DoStuff(), poiché rappresenta il comportamento. Fa cose.


1

Potrebbe non riuscire per diversi motivi, eccezione, condizioni normali, quindi utilizzare questo:

boolean doStuff() - returns true when successful

Importante è documentare cosa significa il booleano.


1

Di solito i metodi restituiscono un OperationResult(o OperationResult<T>) e impostano la IsSuccessproprietà in modo OperationResultappropriato. Se il metodo "solito" è nullo OperationResult, restituisce , se il metodo "solito" restituisce un oggetto, restituisce OperationResult<T>e imposta la Itemproprietà in modo OperationResultappropriato. Suggerirei anche di avere metodi su OperationResultcome .Failed(string reason)e .Succeeded(T item). OperationResultdiventa rapidamente un tipo familiare nei tuoi sistemi e gli sviluppatori imparano a gestirlo.


0

Dipende molto dalla lingua che stai usando. Come per alcune lingue ci sono convenzioni e protocolli che in realtà non esistono in molte lingue o affatto.

Ad esempio, nel linguaggio Objective-C e i nomi dei metodi SDK per iOS / Mac OS X di solito seguono le linee di:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Come puoi vedere, esiste un metodo chiamato viewDidLoad. Preferisco usare questo tipo di denominazione ogni volta che realizzo le mie applicazioni. Questo potrebbe essere usato per verificare se qualcosa doveva succedere come hai detto. Credo che tu stia pensando troppo profondamente a come si chiamano i tuoi metodi. La maggior parte dei metodi restituisce lo stato di ciò che fa, indipendentemente, ad esempio:

In PHP, mysql_connect () restituirà false se la connessione non riesce. Non dice che lo farà, ma lo fa e la documentazione dice che lo fa, quindi non può essere frainteso dal programmatore.


Ah, ma viewDidLoad è più una notifica di richiamata. Gli utenti non lo chiamano per caricare la vista. Piuttosto, viene chiamato dopo il caricamento della vista. Allo stesso modo: isVisible non imposta la visibilità, ma restituisce semplicemente il valore corrente. Non fare nulla.
lilbyrdie,

0

Vorrei suggerire di usare il prefisso "Prova". Questo è un modello ben noto per situazioni in cui il metodo potrebbe avere successo o meno. Questo modello di denominazione è stato utilizzato da Microsoft in .NET Framework (ad esempio TryParseInt).

Ma forse non si tratta di nominare. Si tratta di come si progettano i parametri di input / output del metodo.

La mia idea è che se un metodo ha un valore di ritorno, lo scopo di chiamarlo dovrebbe essere quello di ottenere quel valore di ritorno. Pertanto, è necessario evitare di utilizzare il tipo restituito per indicare lo stato di un'operazione.

In C #, preferisco usare un parametro out. Usando un out sto segnando il parametro come parametro laterale. Specialmente che C # 6 supporterà la dichiarazione di variabili in linea.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.

0

Vorrei scegliere un nome in base al ruolo principale del metodo. Il fatto che quel metodo restituisca un valore booleano non impone il prefisso "Is". Diamo un po 'di codice Java, che usa il prefisso' Is 'abbastanza estensivamente. Dai un'occhiata java.io.File.delete(). Ritorna boolean, ma non viene chiamato isFileDeleted(). Il motivo è che il ruolo principale del metodo è quello di eliminare un file. Qualcuno chiama questo metodo con l'intenzione di eliminare un file.

Il booleano è lì solo per comunicare il risultato del lavoro. D'altra parte vediamo java.util.Map.isEmpty(). Ovviamente, chiami un tale metodo per scoprire se la raccolta è vuota. Non vuoi fare nulla. Vuoi solo scoprire qual è lo stato attuale.

Quindi, personalmente, rimarrei sicuramente DoStuff().

Questa è in qualche modo una questione molto importante per me. Molte volte quando mantengo il codice legacy, mi imbatto in qualcosa che chiamo personalmente "metodo della menzogna". Il nome suggerisce l'azione A, ma il corpo esegue l'azione B. L'esempio più bizzarro di un metodo getter che modifica i dati nel database.

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.