2012-03-19 79 views
0

我有一個調用一個Action<string>TDD與設計諮詢

public Action<string> DisplayError; 

public void MyMethod() 
{ 
    DisplayError("ERROR"); 
} 

在這種方法我想打電話給DisplayError不過,我可以看到,如果DisplayError爲null,將引發異常的方法。

我可以運行一個測試,證明它會拋出異常。

所以我知道我想添加一個if (DisplayError != null)我的代碼,但我覺得這種設計是錯誤的。也許測試應該是不同的?

+0

儘可能避免傳遞/返回null。在這種情況下,您可以將DisplayError默認爲在調用時不執行任何操作的非操作操作。這是一個像症狀一樣的破碎窗口,第一個空檢查導致空檢查開始在各地發生。一個空引用異常應該表示出了什麼問題,應該儘快通過設計或者提高對如何使用庫的認識來加以修復。 – Gishu 2012-03-20 08:34:59

回答

1

與其將Action設置爲公共字段,不如將其設置爲屬性或將其傳遞給方法。然後您可以在那一刻進行空值檢查。我還懷疑,你需要將它設置後進行檢索,所以無論是:

_displayError = (s) => { throw new ArgumentNullException("Please tell me what to do with this exception!"); }; 

public Action<string> DisplayError 
{ 
    set 
    { 
     if (value == null) { throw new ArgumentNullException("I need this!"); } 
     _displayError = value; 
    } 
} 

public void MyMethod() 
{ 
    _displayError("ERROR"); 
} 

或者:

public void MyMethod(Action<string> displayError) 
{ 
    if (displayError == null) { throw new ArgumentNullException("I need this!"); } 
    displayError("ERROR"); 
} 

其中第一項不應該要求從消費者更改任何代碼。

當然,如果您只是與使用您的代碼的團隊合作,那麼不應該根本不需要空值檢查。只要找出誰在錯誤地使用你的代碼,並進行友好的交談就可以發現誤解。防禦性編程並不能替代成員相互信任的團隊,而且空值檢查不如具有明確定義的易用界面那樣有效。

你需要2個測試:

我的類:

  • 應允許消費者以處理任何錯誤
    • 鑑於我將產生無論什麼原因的錯誤(組在這裏)
    • 當我產生一個錯誤(調用方法,讓錯誤處理程序設置一些變量)
    • 然後我應該給錯誤的處理程序(檢查得到了設定的變量)
  • 應該確保一個錯誤處理程序
    • 鑑於我可能會產生一個錯誤(你可以編寫你喜歡這裏,因爲它是不相關的)
    • 當消費者叫我沒有錯誤處理程序(稱爲一個空的錯誤處理方法)
    • 那我應該立即要求消費者不要那樣做(檢查異常)
+0

我已經走了後者的選擇,但我不確定我的測試應該顯示什麼 – Jon 2012-03-19 15:31:04

+0

已更新。在這些「應該」之後命名測試,在評論中填寫「給定,當時,然後」,填補空白。 – Lunivore 2012-03-19 15:38:17

0

我相信正是在Steve Yeggie的一次演講中,我聽到了這樣一個觀點:「靜態類型語言在編譯時確實避免了一大類錯誤,它們並不否定需求進行空值檢查。'

我覺得這完全正常。

我只是在喝我早上的咖啡,所以讓我知道我是否完全錯過了這個標記。

+0

我知道我需要添加代碼來檢查空值,但是我認爲測試或其他東西並不正確。我是否運行了一個測試,證明如果我不分配它引發的動作爲null或者我做了一個測試,顯示它何時被分配執行? – Jon 2012-03-19 15:24:15

+0

我認爲如果您認爲該行爲爲空的可能性可能是一個問題,那麼添加一個測試是合理的。如果它通過直到生產,並且因此而失敗,你會在以後踢自己。 – dwerner 2012-03-19 15:27:56

1

它完全取決於上下文。

這個類的用戶需要來根據合同設置值嗎?然後它應該拋出一個異常。

是可選的嗎?然後你的支票是有效的,但如果你只是默認DisplayError到一個空的動作,你會得到類似的行爲

DisplayError = s => {}; 

你的方法也導致了用戶可以設置DisplayErrornull在這種情況下,可能性,只有你可以決定什麼是有效的。如果您將其設置爲屬性而不是字段,則會爲您提供更多選項。

_displayError = s => {}; 

public Action<string> DisplayError 
{ 
    get { return _displayError; } 
    set 
    { 
     _displayError = value ?? (s => {}); 
     /* or throw on null */ 
    } 
} 
+0

該類將在其中具有Windows窗體控件,並且我認爲我將在窗體上(指更新GUI)分配一個與此操作相同的例程。在表單實現不存在的情況下,我只是測試這個方法/行動 – Jon 2012-03-19 15:29:01

+0

@Jon,如果你或你的團隊成員負責更新GUI的表單,最好確保沒有人調用該類與null相比,它是在它的空檢查。不要編寫不會在生產中使用的東西 - 這隻會使維護成爲一場噩夢。 – Lunivore 2012-03-19 15:40:07

+0

@Jon:您的控件是否會實現一些必需的界面? – 2012-03-19 15:43:22