簡體   English   中英

我應該/應該拋出什么?

[英]What should I throw / expect to be thrown?

我已經完全厭倦了這段時間編寫的所有錯誤代碼,因此看起來我真的需要了解使用異常的正確方法。

讓我們考慮以下示例:

interface IDataSource
{
  string ReadData(int offset);
}

class FileDataSource : IDataSource {...}
class DatabaseDataSource : IDataSource {...}

如果我使用的是實現IDataSource的類的對象,如果由於某種原因ReadData()失敗,我可以期望捕獲哪些異常?

FileDataSource可能會因為沒有文件或文件小於偏移量而失敗。 DatabaseDataSource可能會失敗,因為它無法連接到數據庫,或者該數據庫中沒有必需的表。

當我這樣做時:

var data = dataSource.ReadData(10);

我應該抓住什么? 另一方面,如果我要實現一個新的類FakeDataSource ,如果發生不好的情況該怎么辦?

我有一種感覺,當我從FileDataSource拋出FileNotFoundException或從DatabaseDataSource SqlException ,這意味着封裝違規,因為我知道實現細節。

是否應該將任何IDataSource拋出的所有異常都綁定到此接口? 我的意思是-當我定義任何新的抽象時,是否還應該定義相關的異常? 像這樣:

interface IDataSource { ... }    
abstract class DataSourceException : Exception { ... }

因此,當某人決定實現IDataSource ,他應該只拋出DataSourceException 那是對的嗎? offset值的錯誤如何處理-我還是應該拋出DataSourceException或標准(.NET) ArgumentOutOfRangeException正常嗎?

也將感謝您認為值得的有關錯誤處理的文章的任何鏈接。

您應該只捕獲可以處理的異常。 如果您的IDataSource拋出FileNotFoundException或SqlException並且您無法處理它,請不要捕獲它。

引發異常時,應引發最能描述問題的異常類型。 您想從拋出的異常中了解三件事

  • 什么地方出了錯?
  • 哪里出錯了?
  • 為什么會出錯?

當有效地使用異常時,由拋出的異常類型回答什么,由異常堆棧跟蹤回答哪里,由異常消息回答為什么。 如果您發現自己的例外沒有回答所有三個問題,則很可能是它們沒有得到有效利用。 調試程序時,三個規則將幫助您充分利用異常。 這些規則是:具體,早扔和遲到。

在大多數情況下,使用通用定義的異常,而不是創建自己的異常。 人們將不加解釋地熟悉它們並理解其含義。 但是,在需要封裝特定模塊的詳細信息的情況下,創建自己的異常類型可能會有所幫助。 采用這種方法時,請將原始異常包裝在模塊的異常中,以使信息在重新拋出時不會丟失。

僅在您的庫/應用程序無法執行被要求執行的操作且需要幫助確定是否以及如何恢復的例外情況下,才應引發異常。

您的例外情況應能准確說明發生了什么。 它們應具有足夠的通用性,以通過指出問題確切內容的消息來指示頂級問題。 根據不同類型的操作細分異常類別。

如果在建立連接時遇到任何麻煩,請引發ConnectionException,然后使用該消息指定引發異常的原因。 如果您無法連接到數據庫,則該消息應指示連接。 用戶名不正確? 密碼錯誤? 讓來電者知道。 等等。

如果您的查詢存在問題,例如您要查詢的表不存在,則拋出QueryException。

將DataSource實現中的每個異常都限制為DataSourceException幾乎和拋出Exception類型的拋出每個異常一樣糟糕。 你不會那樣做,對嗎?

我也不會打擾將異常包裝在DataSourceException中。 我認為這給您的實現增加了不必要的復雜性。 它還將使可讀性稍微復雜一點,因為您將擁有一堆看起來像這樣的代碼:

new DataSourceException(new ArgumentException("Range is invalid"), "Range is invalid");

或這個:

try {
  ...
} catch (Exception e) {
  throw new DataSourceException(e, e.Message);
}

然后,對於所有消耗實現的類,您必須捕獲異常,然后查看內部異常以檢查並查看是否要優雅地處理它。

那有意義嗎?

為什么要在該級別上捕捉任何東西?

您應該只因為一個原因而捕獲:您知道如何處理異常。

有人說如果您想登錄就可以捕獲,但是在這種情況下應該重新拋出異常。 但這意味着到處重復相同的日志行-更好的方法是在主循環中記錄一次。 無論如何,該異常都包含堆棧跟蹤。

捕獲並重新拋出DataSourceException是一種典型的Java解決方案,其目的只有一個:滿足Java編譯器的要求。 畢竟,當您要處理原因時,更有用的是FileNotFoundExceptionDataSourceException嗎?

所以我的回答是: 除非您知道如何處理異常,否則不要抓住!

我不認為有任何特定的異常拋出規則。 它可能是更多選擇,對程序員來說非常主觀。 話雖如此,您可以有不同的選擇。 就像您提到的那樣,您可以引發內置異常,也可以從屬於某個組的類(例如實現接口)引發自定義異常。

在這種情況下,您最好拋出自己的自定義異常,因為您確切知道異常的來源以及如何處理它。 (沒有通過接口模型強制執行此操作的實際方法。)再次,這取決於您在異常冒泡時對異常的處理方式。 對此沒有硬性規定,但是我們的想法是應該優雅地處理異常。 我懷疑您正在編寫錯誤的代碼。 發生異常的原因有很多。 數據庫連接失敗不在您手中。 在大多數情況下,數據庫關閉時,應用程序可能無法運行。 除了捕獲異常,記錄日志,通知所需組並正常失敗之外,您無能為力。 錯誤的應用程序將向用戶顯示服務器信息,而不會通知任何人,因此客戶將不得不致電對其進行修復。

PS:我不認為拋出從filedatasource(或sqlds中對應的文件)中找不到的文件不違反封裝。 由於該類是特定用途的類,因此應該知道實現細節。

您應該拋出任何描述發生了什么以及操作失敗的原因的信息。 您應該只期望可以真正處理的異常-絕不要僅僅為了捕獲異常就捕獲異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM