想知道是否有人可以按我的方式提出一些指导。我的标准应用程序设置一直是nTier应用程序(演示文稿,业务,数据和通常的常见)。我已经避免设置和IoC容器(在其他人的应用程序中使用它们很长时间,只是没有设置它们),只要我能,但最终不得不冒险。
我对IoC的理解允许依赖注入,这反过来又可以使单元测试成为可能(更容易),所以在我的脑海中,我想至少在业务层上执行单元测试....但每个例子像StructureMap一样设置IoC使得表示层上的IoC成为可能。那么......我要问的是什么是最佳实践'适用于具有IoC的nTier App。
感谢。
答案 0 :(得分:2)
DI的主要好处不是单元测试(尽管这肯定是一个好处)。主要好处是松散耦合。 “可测试”的应用程序不一定是松散耦合的。
然而,松散耦合带来的不仅仅是可测试性。
疏松耦合的好处
public interface IWriter
{
void WriteSomething();
}
public interface ISomeService
{
void Write();
}
您可以使用Decorator Pattern扩展服务:
public class NullWriter : IWriter
{
public void WriteSomething()
{
// Do nothing - this is a "null object pattern".
}
}
public class HelloWriter : IWriter
{
public readonly IWriter innerWriter;
public HelloWriter(IWriter innerWriter)
{
if (innerWriter == null)
throw new ArgumentNullException("innerWriter");
this.innerWriter = innerWriter;
}
public void WriteSomething()
{
this.innerWriter.WriteSomething();
Console.WriteLine("Hello.");
}
}
public class GoodbyeWriter : IWriter
{
public readonly IWriter innerWriter;
public GoodbyeWriter(IWriter innerWriter)
{
if (innerWriter == null)
throw new ArgumentNullException("innerWriter");
this.innerWriter = innerWriter;
}
public void WriteSomething()
{
this.innerWriter.WriteSomething();
Console.WriteLine("Goodbye.");
}
}
public class SomeService : ISomeService
{
private readonly IWriter writer;
public SomeService(IWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
}
public void Write()
{
this.writer.WriteSomething();
}
}
上面的内容就像是:
// Composition Root
var nullWriter = new NullWriter();
var goodbyeWriter = new GoodbyeWriter(nullWriter);
var helloWriter = new HelloWriter(goodbyeWriter);
var service = new SomeService(helloWriter);
// End Composition Root
// Execute
service.Write();
//Writes:
//Hello.
//Goodbye.
现在,已设置方案,您可以扩展SomeService所做的而不更改任何现有类型。需要更改的应用程序的唯一部分是composition root。
public class HowAreYouWriter : IWriter
{
public readonly IWriter innerWriter;
public HowAreYouWriter(IWriter innerWriter)
{
if (innerWriter == null)
throw new ArgumentNullException("innerWriter");
this.innerWriter = innerWriter;
}
public void WriteSomething()
{
this.innerWriter.WriteSomething();
Console.WriteLine("How are you?");
}
}
// Composition Root
var nullWriter = new NullWriter();
var goodbyeWriter = new GoodbyeWriter(nullWriter);
var howAreYouWriter = new HowAreYouWriter(goodbyeWriter);
var helloWriter = new HelloWriter(howAreYouWriter);
var service = new SomeService(helloWriter);
// End Composition Root
// Execute
service.Write();
//Writes:
//Hello.
//How are you?
//Goodbye.
约定优于配置
DI的另一个(经常被忽视的)好处是Convention over Configuration。将构造函数注入与DI容器组合时,其中许多容器可以自动将ISomeService
映射到SomeService
。一些容器(如StructureMap)也可以构建自己的约定。
好处并不明显,因为在您使用约定注册数十种类型之前,它确实无法获得回报。但是,如果您使用它们,可以大大减少编写应用程序所需的代码量。
N-Tier App
对于单个应用程序,通常只有一个composition root尽可能靠近应用程序的入口点。在MVC中,这将在HttpApplication.Start
方法中。
但是,这可能会有所不同,具体取决于您是将应用程序设计的图层视为DI Friendly Libraries还是DI Friendly Frameworks,以及您是否认为应用程序的某个部分是“插件”在构建之后添加(基本上,创建一个可以加载动态依赖关系的组合根)。
解决此问题通常有3种方法:
Compose
方法调用的每个图层上制作公共Compose
方法。我甚至看到有些人建议将所有“层”放入一个项目中,如果你不打算单独使用这些部分,那就不会有太大的缺点了。 NuGet Gallery是一个以这种方式构建的项目。
恕我直言,将所有内容公开并将组合根目录放在主应用程序中通常是单个多层应用程序的最佳选择,其中的部分不打算与其他应用程序一起使用。
最后的词
如果您认真学习DI,请选择Mark Seemann撰写的书Dependency Injection in .NET。您可能不会认为DI本身就是一个足够大的开发领域,但本书确实提供了许多优势,而不仅仅是DI,例如SOLID原则。