构造函数初始化可以用于依赖注入,而不是使用DI容器吗?

时间:2017-02-02 21:53:19

标签: c# .net design-patterns dependency-injection

这里是我所谈论的一个例子

public interface IService<T>
    where T : class
{
    List<T> GetAll();
    T GetById(object id);
    .......... other methods here
}

public class Service<T> : IService<T>
    where T : class
{
    ... implement interface here
}

public class ServiceClient
{
    private readonly IService<User> _service;

    public ServiceClient(IService<User> service)
    {
        _service = service;
    }

    public ServiceClient() : this(new Service<User>()){}
}

有人可以告诉我这和依赖性解析器之间的区别吗?我通常使用SimpleInjector进行依赖注入,我只是想知道Container对上面做的好处..

由于

更新

好的,我可以说我现在已经设置好了容器并删除了这个&#39;这个&#39;构造函数初始化,我想现在测试ServiceClient

让我们使用MS unit test for snippet

[TestMethod]
public void Given_Something_Should_Success()
{
    // Arrange

    // how do i make an instance of this in a test without adding the 'new Service<User>()' part
    ServiceClient client = new ServiceClient(new Service<User>());
}

并且不更改我的ServiceClient构造函数来执行此操作。甚至可以在这个级别上做到这一点吗?很抱歉,如果这是一个菜鸟问题,我只是想了解一些事情。

public ServiceClient(IService<User> service = null)
{
    _service = service ?? new Service<User>();
}

4 个答案:

答案 0 :(得分:4)

请参阅以下代码行:

public ServiceClient() : this(new Service<User>()){}

现在ServiceClient知道Service<User>类,并且ServiceClient不会在没有Service<User>类所在的程序集的引用的情况下构建的程序集。此外,如果Service<User>有其他方法不在IServiceUser<User>中,那么开发人员可以在课程中执行此操作:

(this._service as Service<User>).SomeMethodNotBelongingToTheInterface();

现在有人可以争辩说,一个不负责任的开发者会做那样的事情,但这远远超出了这一点。

要完全脱离具体实现,您不应该致电new并将其留给Composition Root。您的类和程序集都应该只使用接口,并且完全不了解具体的实现。这可以保证您可以在组合根级别插入任何实现,一切都会正常工作。

这并不意味着即使没有行为的POCO类也应该在接口之后,而只是需要即插即用行为的类。

如果遵循此模式,还可以在Visual Studio中创建体系结构图,并指示哪个层可以引用哪些程序集。在构建期间,如果开发人员创建对不允许引用的程序集的引用,则构建将失败(如果配置为失败)。

在组合根级别,您是否要使用IoC容器将类插入彼此或手动执行操作取决于您。但是,如果有适合您的好工具(IoC容器),为什么要手动操作。您可以拥有一个配置文件,其中包含所有测试类,另一个配置实际类。您可以指示整个程序只使用一行代码运行任何配置:这就是我所说的即插即用。依赖注入,松耦合等背后的整个想法是你的类不应该有针对具体实现编写的代码。

答案 1 :(得分:0)

在Container中,您将注册您的类和界面。 好处是,你有一个ioc类,它将负责为你提供正确的类型。 所以你可以针对界面进行编程。 而且您不需要在代码中添加新内容。 当您必须管理范围或模拟某个类时,这也为您提供了灵活性。 以下是通过IoC(Autofac)注册的一些示例

if __name__ == "__main__"

答案 2 :(得分:0)

使用Container的内容是您在应用程序中的一个集中位置,用于指定应如何解析依赖项。

想象一下,你有许多类想要注入相同的IService实现。通过使用上面的方法,您必须为所有这些类编写相同的代码。如果你曾经使用过一个容器,指定它就足够了。

此外,许多DI为您提供了更多功能,可以共享实例,维护注入的生命周期等,为依赖项提供依赖性等。

答案 3 :(得分:0)

这称为Bastard injection,你不应该这样做,因为它完全违背了依赖性反转的目的:创建解耦和可维护的代码。如果您使用default实现,则意味着您必须引用包含服务实现的项目。或者最糟糕的是,您可能会在它所在的同一个库中创建服务的实现。

另一方面,您可能有一个很好的默认实现。例如,您可以定义此接口:

public interface IClock
{
    DateTime Now { get; }
}

此界面的明显实现是

public class Clock : IClock
{
    public DateTime Now => DateTime.Now;
}

我不会使用公共构造函数,我会通过 Property 注入它,并将构造函数留给另一个实现(可能是测试中的模拟)。我也认为SimpleInjector会有两个构造函数。这是一个让我更多地了解这些想法的工具。

IService的实现与数据库,HTTP服务或远程服务或第三方库或文件系统进行通信时,这些是您创建单独的一个很好的理由项目,引用您定义接口的那个,创建实现,然后在您设置简单注入器的位置,您可以将接口与实现绑定(这需要在此引用两个项目)。如果稍后您需要不同的实现,您可以简单地重复该过程并更改组合根。