2014-04-02 74 views
12

我想找出具有多個結果值的方法的優缺點。最佳實踐返回值vs例外vs枚舉

例如,我正在使用登錄方法。如果登錄成功,它會通過,否則我需要知道它爲什麼失敗。

1.返回true或false(沒有足夠的信息)

bool Login(string user, string password); 

2.返回true,如果它是成功的,否則拋出異常

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

3。什麼都不返回拋出一個異常,如果沒有成功

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

4.返回一個枚舉值

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

「登陸」 僅僅是一種示範性情況。我想知道不同實現的優點和缺點,並且在哪些情況下它們或多或少是合適的。

+0

1和2顯然不是值得考慮的。 #3的意義在於通常情況如何,但唯一能夠認識到可能是#4的人在你的情況下更適合你。 – Jon

+0

API是同步還是異步? –

+0

同步,對不起:) – Jan

回答

3

你會得到更多的自以爲是的答案,如果我在做,我會結合3 & 4.拋出LoginFailedException enum說明原因。

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 
} 

理由選擇例外選項: 假設你選擇返回值唯一的方法,你的API的用戶(可以是客戶,或其他開發者)可以得到一個機會,忽略了API。

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

誰知道他們要做這樣

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

所以,在面對IMO拋出異常說我無法處理您的登錄請求,由於某某原因。簡單一點。

+4

我喜歡這種方法,雖然在imho中引發異常似乎是一種常見場景。我覺得Exceptions應該用在更深層次上出現問題的'特殊'場景中。 – Moeri

+2

@Moeri我認爲這是正確的方式,登錄失敗並不是一個通常的條件,它是一個**異常的**條件,考慮你只是選擇一個Enum apprroach,而你開發一個公共API,如果用戶不'檢查返回值,並調用'Login();'並假定登錄。拋出它面對說heyy出了什麼問題(與sqlserver通過SqlException相同) –

+0

@Moeri有例外情況可以替代'C-Style'狀態碼和'GetLastError()'。你會如何用這種方法實現「內部異常」的想法? *錯誤發生在兩層或更多層的情況* – tchelidze

1

絕對不是例外。登錄失敗幾乎不是一個「例外」情況,它只是應用程序的一個正常邏輯過程。如果使用異常,那麼除了處理登錄失敗的情況之外,您將不得不使用異常處理程序來登錄。這看起來像是使用邏輯流異常的定義,這是不正確的。

如果需要返回特定的信息(這是不是總是必要從登錄功能,但可能會在你的情況下),#4似乎是合理的。你可以把它更進一步,使其成爲一個對象:

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

,或者根據邏輯吧,一個不變的結構,而不是一類。 (確保結構是不可變的,可變結構只是要求麻煩。)問題的關鍵在於,你可以在結果對象本身上應用各種邏輯和功能,這似乎是你的方向。

1

當然這取決於特定的情況,但讓我提供幾個我的主觀意見位置:

  1. 我敢肯定這樣的情況下,應儘量避免。該方法的名稱表示它只執行任何操作,如「登錄」。根據方法名稱,我們在這裏不能指望任何結果。您是否希望該方法返回bool的值,那麼將其命名爲IsLoggedIn(userName)會更好。另外,你永遠不會知道是否需要擴展返回值的集合。所以enum在這裏也要好得多,因爲價值的目標反映在enum的名稱中,而不是簡單的bool

  2. 與上述相同。這裏的異常有助於停止整個執行層次結構(當然可以在調用堆棧中包含多個方法),而不僅僅是返回結果並讓調用者作出適當的決定。對我來說更靈活的解決方案。在目前的情況下,我只會使用異常來進行參數驗證。像「錯誤的用戶名/密碼」等情況並不例外。從用例的角度來看,它們是正常的。 null參數或錯誤的參數格式是例外情況。

  3. 如果您不需要該方法來返回值,那就是要走的路。不要忘記,不應該將異常用作導航。我的意思是UserSuccessfullyCreatedException左右。

  4. 正如我上面提到的那樣,這是最適合我的方式。單點不是將驗證異常置爲enum值。你有例外。

因此,對於驗證enum結果加例外的是很長的路要走。

如果您想方法執行期間,收集所有的錯誤,你可能會喜歡創造特殊LoginOperationResult類來包裝的所有信息(包括驗證errors`方法執行過程中發生。

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

我通常在我的項目中使用這種方法:

簽名:

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

用法:

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

你可以擴展這個來回報您的枚舉:

簽名:

enum LoginResult 
{ 
    Successful 
    UnknownUser, 
    WrongPassword 
} 

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

用法:

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