2014-04-02 13 views
12

Sto cercando di scoprire i vantaggi e gli svantaggi di un metodo con più valori di risultato.Valore restituito best practice vs Exception vs Enum

Ad esempio, sto utilizzando un metodo di accesso. Se l'accesso ha avuto successo, passerà, altrimenti ho bisogno di sapere perché ha fallito.

1. Ritorna vero o falso (Non abbastanza informazioni)

bool Login(string user, string password); 

2. ritorno vero, se fosse successo, altrimenti un'eccezione

public class UnknownUserException : Exception { } 
public class WrongPasswordException : Exception { } 
bool Login(string user, string password); 

3. Non restituire nulla Un'eccezione, se non fosse successo

public class UnknownUserException : Exception { } 
public class WrongPasswordException : Exception { } 
void Login(string user, string password); 

4. Ritorna un valore di enumerazione

enum LoginResult 
{ 
    Successful 
    UnknownUser, 
    WrongPassword 
} 
LoginResult Login(string user, string password); 

"Login" è solo un esempio di caso. Vorrei sapere quali sono i vantaggi e gli svantaggi delle diverse implementazioni e per quali casi sono più o meno appropriati.

+0

1 e 2 non sono ovviamente degno di considerazione. # 3 è nella vena di come si fanno le cose in genere, ma l'unica persona attrezzata per riconoscere che forse # 4 è più appropriato nel tuo caso sei tu. – Jon

+0

API è sincrono o asincrono? –

+0

sincrono, mi spiace :) – Jan

risposta

3

Otterrete risposte più supponenti, Se lo facessi, combinerò 3 & 4. Lanciate LoginFailedException con una enumerazione che spiega perché.

void Login(string user, string password);//Or return a bool(redundant though) 

class LoginFailedException : ApplicationException 
{ 
    public LoginFailReason Reason {get; private set;} 
    public LoginFailedException(LoginFailReason reason) 
    { 
     this.Reason = reason; 
    } 
} 

enum LoginFailReason 
{ 
    UnknownUser, 
    WrongPassword 
} 

motivo per scegliere l'opzione eccezione: Si supponga di scegliere valore di ritorno unico approccio, gli utenti del vostro api (possono essere cliente o altri sviluppatori) possono ottenere la possibilità di trascurare l'API.

instance.Login(user, password); 
var accountInfo = instance.GetAccountInfo();//Assuming logged in; going to explode 

Chissà che devono fare come questo

if(instance.Login(user, password) == LoginResult.Successful)) 
{ 
    var accountInfo = instance.GetAccountInfo(); 
} 

Così, un'eccezione IMO gettare in faccia dicendo che non può elaborare la richiesta di accesso a causa di ragioni così e così. Rendilo semplice.

+4

Mi piace questo approccio, anche se sembra sbagliato gettare un'eccezione in ciò che, sembra, sembra un comune scenario. Credo che le eccezioni dovrebbero essere utilizzate in scenari "eccezionali" in cui qualcosa non va a un livello più profondo. – Moeri

+2

@Moeri Credo che sia giusto, l'errore di accesso non è una condizione normale è una condizione ** eccezionale **, considera che stai scegliendo solo un apprendista Enum e sviluppi un'API pubblica, e se l'utente non lo fa t controlla il valore restituito e chiama semplicemente 'Login();' e presume che abbia effettuato l'accesso.? Buttalo in faccia dicendo heyy qualcosa è andato storto (come lo fa sqlserver tramite SqlException) –

+0

@Moeri Le eccezioni sono lì per sostituire i codici di stato 'C-Style' e' GetLastError() ''s. Come implementeresti l'idea di "Inner Exception" con questo approccio? * caso in cui si verifica un errore a due o più livelli * – tchelidze

1

Sicuramente non eccezioni. Un login fallito non è un caso "eccezionale" e si tratta solo di un normale corso logico per l'applicazione. Se si utilizza un'eccezione, sarà sempre necessario eseguire il wrapping del log in con un gestore di eccezioni per nessun altro motivo che per gestire il caso di un accesso non riuscito. Sembra proprio la definizione dell'uso di eccezioni per il flusso logico, il che non è corretto.

Se è necessario restituire le informazioni specifiche (che non è sempre necessaria da una funzione di login, ma potrebbe essere nel tuo caso), # 4 sembra ragionevole. Si potrebbe prendere un ulteriore passo avanti e renderlo un oggetto:

public class LoginResult 
{ 
    // an enum for the status 
    // a string for a more specific message 
    // a valid user object on successful login 
    // etc. 
} 

Oppure, a seconda della logica per esso, una struct immutabile, al posto di una classe. (Assicurati che la struttura sia immutabile, che le strutture mutabili stiano solo chiedendo dei problemi). Il punto è che puoi applicare tutti i tipi di logica e funzionalità sull'oggetto risultato stesso, che sembra essere la direzione verso cui ti stai dirigendo.

1

Naturalmente dipende dalla certa situazione, ma mi permetta di fornire un paio di miei commenti soggettivi qui:

  1. Sono sicuro che questi casi dovrebbero essere evitati. Il nome del metodo indica che esegue semplicemente qualsiasi operazione come "Login". Secondo il nome del metodo non possiamo aspettarci alcun risultato qui. Ti piacerebbe che il metodo per restituire il valore bool è molto meglio chiamarlo come IsLoggedIn(userName). Inoltre non saprai mai se è necessario estendere il set di valori restituiti. Pertanto, lo enum è molto meglio anche tenendo conto del fatto che l'obiettivo del valore si riflette nel nome enum anziché nel semplice bool.

  2. Come sopra. Le eccezioni qui aiutano a fermare l'intera gerarchia di esecuzione (che può naturalmente contenere più di 1 metodo nello stack di chiamate) invece di restituire semplicemente il risultato e lasciare che il chiamante prenda la decisione appropriata. Soluzione più flessibile per me. Nel caso corrente userei le eccezioni solo per la validazione dei parametri. Situazioni come "nome utente/password errati" non sono eccezionali. Sono normali dal punto di vista dei casi d'uso. Il parametro null o il formato parametro errato sono casi eccezionali.

  3. Se non è necessario il metodo per restituire un valore, quella è la strada da percorrere. Non dimenticare che non devi utilizzare le eccezioni come navigazione. Voglio dire UserSuccessfullyCreatedException o così.

  4. Come ho già detto, questo è il modo migliore per me. Il punto singolo non deve posizionare le eccezioni di convalida come valori enum. Hai delle eccezioni per questo.

Così enum risultato più eccezioni per la convalida è un modo per andare.

Se vuoi per raccogliere tutti gli errori durante l'esecuzione metodo che probabilmente piace creare classe speciale LoginOperationResult per avvolgere tutte le informazioni (compresi validazione errors` verificato durante l'esecuzione del metodo.

class OperationResult 
{ 
    public OperationStatus Status { get; set; } 

    public IEnumerable<ValidationError> Errors { get; set; } 
    // or list of exceptions 
} 

class LoginOperationResult : OperationResult 
{ 
    // Login result specific data. 
} 

enum OperationStatus 
{ 
    Success, 
    Denied, 
    ValidationFailed, 
    // etc. 
} 
0

io di solito uso questo approccio nei miei progetti:

firma:

bool TryLogin(string username, string password, out User user); 

utilizzo:

User user; 
if(userService.TryLogin(username, password, out user))) 
{ 
    // do stuff with user 
} 
else 
{ 
    // show "login failed" 
} 

Si potrebbe espandere questa per riportare l'Enum:

firma:

enum LoginResult 
{ 
    Successful 
    UnknownUser, 
    WrongPassword 
} 

LoginResult TryLogin(string username, string password, out User user); 

utilizzo:

User user; 
LoginResult loginResult; 
if((loginResult = userService.TryLogin(username, password, out user)) == LoginResult.Successful) 
{ 
    // do stuff with user 
} 
else 
{ 
    // do stuff with loginResult 
}