我知道如何使用依赖注入,但我认识到它没有实际优势

本文关键字:认识到 何使用 依赖 注入 我知道 | 更新日期: 2023-09-27 18:07:56

这是关于这个的(注入依赖关系(

private readonly ICustomerService _customerService;
public Billing(ICustomerService customerService)
{
   _customerService = customerService;
}

与此(创建依赖项(

private readonly ICustomerService _customerService;
public Billing()
{
   _customerService = new CustomerService();
}

后一个样本,所以他们说是不好的,因为...它违反了DI...当然什么都没有注入...但是,如果 DI 不存在,那么从计费类手动创建客户服务有什么不好?我认为在服务接口的可交换性方面没有实际优势。

我要求一个带有源代码的实际示例,它可能是单元测试或显示实际解决方案,为什么它如此松散耦合。

任何热衷于展示他的DI肌肉的人以及为什么它具有存在和应用的实际权利?

更新

所以人们不必全部阅读,我将在这里写下我的短暂经历:

DI 作为一种模式具有实际用途。要遵循DI,不要手动注入所有服务(他们说这是一个可怜的人DI工具......(,请使用像LightCore/Unity这样的DI框架,但请确保为适当的工作使用正确的工具。这是我没有做的;-(开发 mvvm/wpf 应用程序 我还有其他需求,LightCore/Unity 工具无法支持它们甚至是一个障碍。我的解决方案是使用我很高兴的MEFEDMVVM。现在我的服务是在运行时自动注入的,而不是在启动时

我知道如何使用依赖注入,但我认识到它没有实际优势

理解如何和理解为什么是非常不同的事情。

DI 的最大好处之一是用于单元测试。在第二个示例中,如果不测试客户服务(并测试链中的任何其他依赖项(,就不可能对计费进行单元测试。在这种情况下,您不是在进行单元测试,而是在进行集成测试!如果你想要一个使用 DI 的好理由,你只需要寻找单元测试的理由。

想象一下,CustomerService连接到您的CRM系统和数据库。 它创建一大堆网络连接来检索有关客户的数据,并可能从数据库中读取其他内容以增强这些数据,然后再将数据返回到Billing类以用于其计算。

现在你想对Billing进行单元测试,以确保它所做的计算是正确的(你不想发送错误的账单,对吗?

如果构造函数绑定到需要连接到真实 CRM 系统和数据库的类,您将如何Billing进行单元测试? 将该依赖项注入为接口,轻松允许您为测试提供模拟版本不是更好吗?

这就是 DI 有用的原因。

DI 在您想要

将接口的不同实现传递给您的类时很有用,例如:单元测试。

假设您的计费构造函数是 MVC 控制器的构造函数,并且您的客户服务采用某种形式的 IDataContext 作为参数。

全球

// Does the binding
ICustomerService binds to CustomerService
IDataContext binds to EntityFrameworkContext

客户服务

private IDataContext _datacontext;   
public CustomerService(IDataContext dataContext)   
{   
   _dataContext = dataContext;  
}
public AddCustomer(Customer entity)   
{   
  this._dataContext.Customers.Add(entity);
  this._dataContext.SaveChanges;   
}   

MVC 控制器

private ICustomerService _customerService;
public Billing(ICustomerService customerService)
{
   _customerService = customerService;
}
public ActionResult NewCustomer()
{
  Customer customer = new Customer(){ Name = "test" };
  this._customerService.AddCustomer(customer);
  return View();
}

假设您要对服务或控制器进行单元测试。您将传入客户服务,但您将传入EntityFrameWorkContext的虚假实现。因此,实现IDataContext的FakeDbContext被传递给客户服务。

FakeDbContext 可能只是将实体存储在列表或更复杂的存储机制中,关键是,您可以注入不同的依赖项实现,这允许您更改一个组件的行为,而无需在其他地方修改代码。

根据我的经验,这不仅是关于避免集成测试(但这也是一个非常重要的点(。在内部实例化类可以创建大量的工作单元测试。像 CustomerService 这样的类可能依赖于打开的数据库连接、配置文件、可用的服务以及许多其他内容,当您的工作是仅测试计费类时,您不必了解这些内容。

话虽如此,有时注射所有东西总是很痛苦。注入框架可能会减轻这种负担,但我不是大粉丝。另一位好心的stackoverflow用户向我指出了他所谓的"穷人注射"。基本上,它由两个构造函数重载组成:一个带有注入接口的构造函数,另一个没有。没有的那个除了实例化实现接口的具体类并将其传递给另一个构造函数之外什么都不做。它是这样的:

public class Billing
{
  ICustomerService _customerService;
  public Billing():this(new CustomerService()) {}
  public Billing(ICustomerService customerService)
  {
    _customerService = customerService;
  }
}

这样,您就可以在测试时注入一种方法,并且可以使用接口的默认实现构造类。不是每个人都喜欢这种模式,但我发现它在某些情况下很实用。