2009-12-14 68 views
3

這一直困擾我一段時間了。讓我們想象一個表示資源的類,爲了能夠使用這個資源,需要先調用它的'Open'方法,否則將拋出InvalidOperationException異常。即使代碼不會造成危害,代碼是否應該阻止邏輯上無效的呼叫?

我的代碼還應該檢查是否有人試圖打開已打開的資源,或關閉已關閉的資源?

代碼應該防止邏輯無效的調用,即使不會造成危害嗎?

我認爲這種編程方式有助於在另一側編寫更好的代碼,但我覺得我可能會承擔過多的責任並影響可重用性。

你們認爲什麼?

編輯:

我不認爲這可以被稱爲防禦式編程,因爲它不會讓一個可能不好用要麼打滑,另外InvalidOperationException異常將被拋出。

回答

6

這叫做defensive programming。這是一個很好的編程習慣,因爲您可以確保您的應用程序不會因不當行爲而崩潰。

在調用另一個方法之前應先調用某個方法,這不是一個好的編程習慣。它增加了很多複雜性,這更好地由類本身處理。

這叫做sequential coupling。這篇維基百科文章說,如果這是一種不好的做法,它取決於上下文,但是如果處理不當,它不應該崩潰。有時候有必要拋出一個例外來說清楚。

+0

那麼不是真的。當你期望可能的濫用(這意味着當你與界面另一側的unknwon(ab)用戶進行交互)時,使用防禦性編程來防止崩潰。但是,OP詢問open是否應該處理濫用,因爲什麼也沒有發生。我不同意 - 應該告知用戶他的代碼有問題(無論是錯誤代碼,斷言還是例外)。 總之:處理(使用防禦性編程),但通知。吞下或崩潰,API用戶會討厭你。 – MaR 2009-12-14 12:38:53

+0

我沒有說任何關於吞嚥錯誤的東西。該應該意識到發生的錯誤並採取適當的行動。 – Ikke 2009-12-14 12:56:42

+0

有趣的是,我在哪裏可以找到更多有關這是一個不好的做法的信息? – Trap 2009-12-14 22:37:48

2

如果你的例子真的是這樣,那麼Open功能應該可以由類的構造函數調用。

如果您考慮C++ iostream庫(它被廣泛使用並被認爲是一個很好的例子),您可以調用流類的任何操作,無論它是否打開。如果操作無法執行,被調用函數將簡單地返回某種失敗指示符。當然,這些函數必須測試流狀態才能完成此操作。

你不能做的是讓你的程序靜靜地接受任何舊的輸入作爲參數。例如,這將是一個破碎的實現的strlen()

int strlen(const char * s) 
{ 
    if (s == 0) 
    { 
     return 0;  // bad 
    } 
    else 
    { 
     // calculate length not shown 
    } 
} 

,因爲它字段無效輸入,而不會引起大驚小怪 - 這應拋出一個異常,或使用一個assert(),根據您的具體的發展理念。

+0

打開/關閉功能在那裏,因爲您可能需要註冊和/或聲明資源,但直到耗盡時才能完全初始化。我喜歡延遲初始化的想法,只是離開了Close函數:) – Trap 2009-12-14 22:52:25

+0

這實際上是C++文件流對象的工作原理。您可以在構建時或之後使用其open()成員打開它。但是,無論哪種情況(打開或不打開),流始終處於有效狀態。 – 2009-12-15 09:20:45

1

在確定您的代碼中應該有多少安全檢查以獲得貴組織的最佳成本/收益比時,無論是品味,人才還是經驗都無可替代。

一個好的質量APIs預計是傻瓜證明,並引導用戶適量的警告。

有時,安全預防措施可能會影響性能。性能是編程中最不直觀的事情之一。只有在性能真正重要時才能進行優化。

3

這真的取決於班級的實際操作。在某些情況下,靜靜地失敗是一個好主意(例如,您希望DVD播放器繼續工作,如果它打開已打開的DVD托盤不會顯示錯誤信息),而在其他情況下,您希望獲得儘可能多的信息(例如,如果一架飛機試圖關閉據報道已關閉的門,則出現問題,應通知飛行員)。

在大多數情況下,在執行邏輯無效操作時引發錯誤對開發人員非常有用,因此實現這些異常取決於誰將使用代碼。如果它在內部用於一個應用程序,那麼它並不重要。但是,如果它被許多不同的項目或開發人員使用,那麼我會研究它。

1

如果這是公開發布的SDK的一部分,那麼暴露的API調用應該具有較強的驗證。它將幫助你的'用戶'(誰是開發者),並確保你不會被支持的用法,你從來沒有打算支持。

否則,我不會添加這樣的檢查。我認爲他們讓代碼難以閱讀,而且這些檢查很少被測試。在過去,我會添加很多這樣的代碼,以確保我的代碼不會做錯誤的事情。現在我編寫單元測試來驗證我的代碼是否正確。區別?我認爲測試更易於維護,更具可讀性,並且不會混淆您的生產代碼。

1

在打開已打開的文件的情況下,它取決於瞭解請求的效果,它是否會重置當前讀取位置,例如。

在關閉已經關閉的文件的情況下,可以將其視爲要將文件置於已知狀態的請求。代碼不需要做任何事情,但所需的狀態是成功的,所以代碼可以返回成功條件。如果需要處理某種文件緩衝或者可能需要互連鏈接的資源進行協調,例如調制解調器/串行端口或打印機/假脫機程序,則不是這樣。

退後一步,根據期望的結果考慮問題,包括任何副作用。

我們曾經在顯示的應用程序菜單上放置了一個「註銷」鏈接,無論您的登錄狀態如何。爲什麼?因爲它只需要一個簡單(很短)的方法來處理從登錄屏幕返回到登錄屏幕並保存了大量的檢查來處理跟蹤登錄狀態,所以僅當「登出」菜單項被顯示時纔會顯示你已登錄。

+0

國際海事組織,如果作爲用戶我試圖關閉一個已經關閉的流,這可能意味着我是否懶得檢查我自己的狀態,或者在我的代碼中也有不合邏輯的東西。如果我想請求一個關閉操作,我寧願調用RequestClose()並等待事件發生:) – Trap 2010-06-08 15:47:30

1

邏輯無效調用應該總是報在調試模式下用戶..

當在釋放模式編譯的,你的代碼應該不會引發任何不必要的異常或做任何可能危及整個應用。 我個人更喜歡有某種日誌文件,並且記錄這種邏輯上無效的調用肯定不會造成傷害(至少在性能不重要時)