2009-12-29 53 views
2

我創建一個CSV閱讀器(是的,我知道快速CSV閱讀器和FileHelpers)。 CsvReader類使用CsvParser類來解析CSV文件。我想讓CsvReader類的單元可測試,所以我希望能夠在外部設置使用的CsvParser類(也可以創建自己的實現)。我也不想創建解析器並在正常使用時傳遞它。使用控制反轉的對象的創建

這就是我想如何使用它。

var reader = new CsvReader("path/to/file.csv"); 

當這樣做時,我可以在CsvReader的構造函數中創建CsvParser,並有一個屬性來更改解析器。

public ICsvParser Parser { get; set; } 

public CsvReader(filePath) 
{ 
    Parser = new CsvParser(filepath); 
} 

但是當單元測試時,默認的解析器總是被創建,我只想測試CsvReader。

解析器可以傳入構造函數,但我不想在正常使用時單獨創建解析器。這似乎是一個工廠的好地方。

這似乎是使用IOC時常見的問題。什麼是這個好的解決方案?

回答

5

的解決方法是重新編寫的CsvReader構造函數接受ICsvParser的實施和您的具體落實ICsvParser應該有一個構造函數以在其依賴關係(一個文件路徑解析),並已構建ICsvParser應注射到構造爲CsvReader

public CsvReader(ICsvParser parser) { 
    this.Parser = parser; 
} 

ICsvParser應該已經被構建爲接受它的依賴(該文件的路徑被解析)。

正是如此:

// path is string containing path to file to parse 
ICsvParser parser = new SomeCsvParser(path); 
ICsvReader reader = new CsvReader(parser); 

的一點是,CsvReader不需要的路徑,它只是需要一個CsvParser。此外,CsvReader應該不需要知道CsvParser的依賴關係(即它需要一個文件解析路徑),以免它也依賴於這些依賴關係。

new裏面的構造函數是一種氣味。

+0

我同意這一點,但我不希望圖書館的消費者在創建閱讀器時不得不創建解析器。是否最好有另一個構造函數,我只是通過路徑,它創建解析器並使用給定的路徑?或者使用一個接受文件路徑的工廠會更好,並創建閱讀器和解析器並返回閱讀器? –

+1

如果你不想用戶必須創建一個解析器,那麼是的,你最好的選擇是有一個工廠,它會吃掉路徑並返回一個構造的'ICsvReader'。正如我所描述的,您仍然應該設置'CsvParser'和'CsvReader'。我說構造函數裏面的'new'是一個小的。第二種氣味是構造函數中的對象圖構造。也就是說,'CsvReader'的構造函數不應該接受其他對象的依賴('CsvParser')並使用它來構造它自己。該工作應委派給工廠。 SRP和所有這一切。 – jason

+0

太好了。我原來是這樣設置的,但我不喜歡要求解析器傳遞到構造函數中才能正常使用。我只是使用工廠進行簡單/默認創建,一切都應該很好。 –

0

創建兩個構造函數,一個使用parser接口作爲參數,另一個使用filePath。讓第二個創建CsvParser並讓它與此對象一起調用第一個。您的測試代碼然後可以使用第一個構造函數並傳遞一個模擬CsvParser。

該解決方案有一個缺點:包含CsvReader的程序集必須引用包含CsvParser的程序集。你必須自己決定這種情況是否正常。

0

通常,您將通過構造函數傳遞解析器並使用IoC容器創建對象,然後將解析器實例注入構造函數中。

在單元測試中,您可以直接傳入模擬解析器,例如,新的CsvReader(新的MockParser()),或者通過配置你的IoC來爲你注入模擬的測試配置。

3

如果這是IoC,你會傳入CsvParser而不是路徑。