创建使用控制反转的对象

时间:2009-12-29 15:53:27

标签: c# dependency-injection inversion-of-control

我正在创建一个CSV阅读器(是的,我知道Fast CSV Reader和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时的常见问题。对此有什么好的解决方案?

4 个答案:

答案 0 :(得分: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是一种气味。

答案 1 :(得分:3)

如果这是IoC,你将传递CsvParser而不是路径。

答案 2 :(得分:0)

创建两个构造函数,一个使用解析器接口作为参数,另一个只使用filePath。让第二个创建CsvParser并让它用这个对象调用第一个。然后,您的测试代码可以使用第一个构造函数并传递模拟CsvParser。

此解决方案有一个缺点:包含CsvReader的程序集必须引用包含CsvParser的程序集。在你的情况下,你必须自己决定是否可以。

答案 3 :(得分:0)

通常你会通过构造函数传入解析器并使用IoC容器创建你的对象,然后这会将解析器实例注入到构造函数中。

在单元测试中,您可以直接传入模拟解析器,例如新的CsvReader(新的MockParser())或者为你的IoC配置一个为你注入模拟的测试配置。

相关问题