在this question阅读了很好的答案后,我观看了Justin Etheredge的截屏视频。这一切看起来都非常好,只需最少的设置即可从代码中获得DI。
现在我的问题是:你为什么要使用不使用配置文件的DI框架?这不是使用DI基础设施的重点,以便您可以在构建/发布/无论代码之后改变行为(“策略”,可以这么说)吗?
任何人都可以给我一个很好的用例,使用像Ninject这样的非配置DI进行验证吗?
答案 0 :(得分:10)
我认为你不需要没有配置的DI框架。我想你想要一个带有所需配置的DI框架。
我以春天为例。回到过去的“旧时代”,我们过去常常将所有内容放在XML文件中,以使所有内容都可配置。
当切换到完全注释的制度时,您基本上定义了应用程序包含的角色组件。所以一个给定的 服务可以例如具有一个用于“常规运行时”的实现,其中存在属于的另一个实现 在应用程序的“Stubbed”版本中。此外,在进行集成测试时,您可能正在使用第三种实现方式。
以这种方式查看问题时,您很快意识到大多数应用程序只包含一组非常有限的组件角色 在运行时中 - 这些是实际导致要使用的组件的不同版本的东西。通常,组件的给定实现 始终绑定到此角色;它确实是该实施的存在理由。
因此,如果您让“配置”只指定您需要的组件角色,那么您可以在没有更多配置的情况下离开。 当然,总会有异常,但是你只需要处理异常。
答案 1 :(得分:5)
我正在使用krosenvold的路径,这里只使用较少的文本:在大多数应用程序中,每个所需的“服务”只有一个实现。我们根本不编写应用程序,其中每个对象需要每个服务的10个或更多实现。因此,有一个简单的方法说“这是默认实现,使用此服务的所有对象的99%将对它感到满意”是有意义的。
在测试中,您通常使用特定的模型,因此不需要任何配置(因为您手动接线)。
这就是配置约定。大多数情况下,配置只是转储重复DI框架应该知道的东西:)
在我的应用程序中,我使用类对象作为查找实现的键,而“键”恰好是默认实现。如果我的DI框架在配置中找不到覆盖,它将尝试实例化密钥。有超过1000个“服务”,我需要四个覆盖。那将是很多无用的XML。
答案 2 :(得分:3)
使用依赖注入单元测试变得非常简单,因为您可以在测试对象中注入模拟而不是真实对象。您不需要进行配置,只需在单元测试代码中创建并注入模拟。
答案 3 :(得分:3)
我在我的博客上收到了此评论,来自Nate Kohari:
很高兴您正在考虑使用Ninject! Ninject采取的立场 你的DI框架的配置是 实际上是你的申请的一部分,和 不应公开配置。如果 你想要某些绑定 可配置,你可以轻松地使你的 Ninject模块读取你的app.config。 在代码中使用绑定可以节省您的时间 从XML的冗长,并给出 你键入安全性,可重构性和 智能感知。
答案 4 :(得分:1)
您甚至不需要使用DI框架来应用依赖项注入模式。如果除了重新编译代码之外不需要可配置性,您可以简单地使用静态工厂方法来创建对象。
所以这一切都取决于您希望应用程序的可配置性。如果你想要它是可配置/可插入的而无需重新编译代码,你需要一些可以通过文本或xml文件配置的东西。
答案 5 :(得分:1)
我将使用DI进行测试。我现在只考虑使用DI进行测试,因为我们的应用程序不需要任何基于配置的灵活性 - 目前它还太大而无法考虑。
DI倾向于导致更清洁,更分离的设计 - 并且全面提供优势。答案 6 :(得分:0)
如果您想在发布版本之后更改行为,那么您将需要一个支持外部配置的DI框架,是的。
但我可以想到其他不需要此配置的方案:例如,控制业务逻辑中组件的注入。或者使用DI框架使单元测试更容易。
答案 7 :(得分:0)
您应该阅读PRISM in .NET(这是在.NET中执行复合应用程序的最佳实践)。在这些最佳实践中,每个模块将其实现类型“公开”到共享容器中。这样,每个模块都对“谁为这个接口提供实现”有明确的责任。我想如果你能理解PRISM是如何工作的,那就很清楚了。
答案 8 :(得分:0)
当您使用控制反转时,您正在帮助您尽可能少地完成课程。假设您有一些等待文件的Windows服务,然后对该文件执行一系列处理。其中一个过程是将其转换为ZIP,然后通过电子邮件发送。
public class ZipProcessor : IFileProcessor
{
IZipService ZipService;
IEmailService EmailService;
public void Process(string fileName)
{
ZipService.Zip(fileName, Path.ChangeFileExtension(fileName, ".zip"));
EmailService.SendEmailTo(................);
}
}
为什么这个类需要实际进行压缩和电子邮件发送,当你可以有专门的类来为你做这个?显然你不会,但这只是我的观点: - )
除了不实现Zip和电子邮件之外,为什么课程应该知道哪个类实现了服务?如果将接口传递给该处理器的构造函数,那么它永远不需要创建特定类的实例,它将获得完成工作所需的一切。
使用D.I.C.你可以配置哪些类实现某些接口,然后让它为你创建一个实例,它会将依赖项注入到类中。
var processor = Container.Resolve<ZipProcessor>();
因此,现在不仅让您将类的功能与共享功能完全分开,而且还阻止了使用者/提供者对彼此的任何明确了解。这使得阅读代码更容易理解,因为同时考虑的因素较少。
最后,在进行单元测试时,您可以传入模拟的依赖项。当您测试ZipProcessor时,您的模拟服务将仅断言该类尝试发送电子邮件而不是真正尝试发送电子邮件。
//Mock the ZIP
var mockZipService = MockRepository.GenerateMock<IZipService>();
mockZipService.Expect(x => x.Zip("Hello.xml", "Hello.zip"));
//Mock the email send
var mockEmailService = MockRepository.GenerateMock<IEmailService>();
mockEmailService.Expect(x => x.SendEmailTo(.................);
//Test the processor
var testSubject = new ZipProcessor(mockZipService, mockEmailService);
testSubject.Process("Hello.xml");
//Assert it used the services in the correct way
mockZipService.VerifyAlLExpectations();
mockEmailService.VerifyAllExceptions();
所以简而言之。你想要这样做 01:防止消费者明确知道哪个提供者实现了所需的服务,这意味着当您阅读代码时,一次性理解就越少。 02:使单元测试更容易。
皮特