如何重新设计C#应用程序以避免构造函数注入过载



我还是DI和单元测试的新手。我的任务是将单元测试添加到我们的一些遗留代码中。我正在开发WCF web服务。必须进行大量的重构。怪物类可以分为不同的类。怪物方法拆分为多个方法。最后,为外部依赖项创建接口类。最初这样做是为了方便对单元测试的依赖关系进行嘲讽。

随着我的深入,依赖关系列表不断增长。我还有其他web服务要调用,SQL Server和DB2数据库要交互,一个配置文件要读取,一个日志要写入,以及从Sharepoint数据读取。到目前为止,我有10个依赖项。每次我添加一个,它都会破坏我所有的单元测试,因为构造函数中有一个新参数。

如果有帮助的话,我将使用.Net 4.5、Castle Windsor作为我的IOC、MSTest和Moq进行测试。

我看了这里如何避免依赖注入构造函数的疯狂?但这并不能提供任何真正的解决方案。只是说"你的课可能做得太多了。"我研究了Facade和Aggregate服务,但这似乎只是在原地踏步。

因此,我需要一些关于如何使这个类"更少"但仍然提供相同输出的帮助。

public AccountServices(ISomeWebServiceProvider someWebServiceProvider,
ISomeOtherWebProvider someOtherWebProvider,
IConfigurationSettings configurationSettings,
IDB2Connect dB2Connect,
IDB2SomeOtherData dB2SomeOtherData,
IDB2DatabaseData dB2DatabaseData,
ISharepointServiceProvider sharepointServiceProvider,
ILoggingProvider loggingProvider,
IAnotherProvider AnotherProvider,
ISQLConnect SQLConnect)
{
_configurationSettings = configurationSettings;
_someWebServiceProvider = someWebServiceProvider;
_someOtherWebProvider = someOtherWebProvider;
_dB2Connect = dB2Connect;
_dB2SomeOtherData = dB2SomeOtherData;
_dB2DatabaseData = dB2DatabaseData;
_sharepointServiceProvider = sharepointServiceProvider;
_loggingProvider = loggingProvider;
_AnotherProvider = AnotherProvider;
_SQLConnect = SQLConnect;
}

几乎所有这些都存在于其他组件中,但我需要能够在主应用程序中使用它们,并在单元测试中模拟它们。

以下是其中一种方法的布局。

public ExpectedResponse GetAccountData(string AccountNumber)
{
// Get Needed Config Settings
...
// Do some data validation before processing data
...
// Try to retrieve data for DB2
...
// Try to retrieve data for Sharepoint
...
// Map data to response.
...
// If error, handle it and write error to log
}

其他方法非常相似,但它们可能会接触到SQL Server或一个或多个web服务。

理想情况下,我想要的是一个应用程序的例子,该应用程序需要很多依赖项,具有单元测试,并且避免了不断向构造函数添加新的依赖项,从而导致您更新所有单元测试以添加新参数。

感谢

不确定这是否有帮助,但我提出了一个称为GetTester的模式,它封装了构造函数,使处理参数变得更容易。这里有一个例子:

private SmartCache GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis)
{
memory = new Mock<IMemoryCache>();
redis = new Mock<IRedisCache>();
return new SmartCache(memory.Object, redis.Object);
}

如果他们需要所有的模拟,来电者看起来是这样的:

SmartCache cache = GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis);

或者,如果他们不这样做:

SmartCache cache = GetTester(out _, out _);

如果您有构造函数更改,这些仍然会中断,但您可以创建重载以最大限度地减少对测试的更改。这很麻烦,但比其他情况下更容易。

所以您的类可能做得太多了。如果你发现你不断增加一个类正在做的工作,因此需要提供额外的对象来帮助完成这些额外的任务,那么这可能就是问题所在,你需要考虑分解工作。

然而,如果不是这样,那么另一种选择是让类引用依赖类,该依赖类提供对实现各种接口的实例化具体对象或可用于构造对象的实例化工厂对象的访问。然后,您可以不不断地传递新参数,而只传递单个对象,然后根据需要从中提取或创建对象。

最新更新