Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 依赖注入和特定依赖实现_C#_.net_Dependency Injection - Fatal编程技术网

C# 依赖注入和特定依赖实现

C# 依赖注入和特定依赖实现,c#,.net,dependency-injection,C#,.net,Dependency Injection,首先让我介绍无依赖注入的实现(这将打破依赖倒置原则): 要使此代码可测试,请插入IChecksumGenerator: public class MyValidator { private readonly IChecksumGenerator _checksumGenerator; public MyValidator(IChecksumGenerator checksumGenerator) { _checksumGenerator = checksumGenerato

首先让我介绍无依赖注入的实现(这将打破依赖倒置原则):

要使此代码可测试,请插入IChecksumGenerator:

public class MyValidator
{
  private readonly IChecksumGenerator _checksumGenerator;

  public MyValidator(IChecksumGenerator checksumGenerator)
  {
    _checksumGenerator = checksumGenerator; 
  }

  ...
}
现在,如果需要,我们可以轻松地测试MyValidator和存根checksumGenerator。但MyValidator实现在算法上与特定的IChecksumGenerator实现耦合(它只是不能与任何其他实现一起工作)。因此出现了一些问题:

  • 我们引入了一种可能性,即将注入不正确的IChecksumGenerator(例如,由于IoC容器配置错误)
  • 我们打破了封装,因为MyValidator的私有实现细节(耦合到MyChecksumGenerator)超出了类的范围
  • 我找到的最佳解决方案如下:

    public class MyValidator
    {
      private readonly IChecksumGenerator _checksumGenerator;
    
      public MyValidator()
      {
        _checksumGenerator = new MyChecksumGenerator;
      }
    
      internal MyValidator(IChecksumValidator checksumValidator)
      {
        _checksumValidator = checksumValidator;
      }
    
      ...
    }
    
    在这里,我引入了用于测试目的的特殊构造函数(这样我可以在测试中存根IChecksumValidator),但公共构造函数创建了它所耦合的实现(这样封装就不会被破坏)。为测试目的创建一些代码有点难看,但在这种情况下似乎是有意义的


    如何解决这个问题?

    您需要将测试代码与产品代码分开

    在产品代码中,您可以使用:

     var validator = new MyValidator(new MyChecksumGenerator());
    
    在测试代码中:

    var validator = new MyValidator(new MyChecksumGeneratorStub());
    

    其中MyChecksumGeneratorStub实现IChecksumGenerator。

    这并不违反封装。验证通常涉及校验和

    我不会担心配置错误的ioc容器,因为在交付生产的产品中不存在伪造或模拟的实现。烟雾测试将立即发现这一点


    希望这有帮助

    我看不出MyValidator是如何通过算法耦合到IChecksumGenerator的。IChecksumGenerator将向MyValidator提供一个合同,给定一组特定的输入,将返回一组特定的输出

    IChecksumGenerator的实现如何计算这些输出与MyValidator无关。这就是为什么您能够提供一个测试存根。测试存根在输入和输出之间有一个硬编码映射,使您能够进行测试。真正的实现将使用一个算法

    一个算法可以有许多不同的实现。可以有一个优化内存使用的实现,还有一个优化速度的实现

    只要为每个可能的输入提供了正确的输出,MyValidator就不应该关心实现。确保这一点是测试的全部内容


    HPower,如果它真的是算法耦合的,并且没有办法将两者分开,那么它们可能不应该是单独的类。

    重构到构造函数注入是一个非常好的主意,但我发现问题中提出的约束很奇怪。我建议你重新考虑一下这个设计


    如果MyValidator只与IChecksumGenerator的一个特定实现一起工作,那么它将违反(LSP)。本质上,这也意味着您将无法注入,因为stub/mock/fake/whatever不是“正确的”IChecksumGenerator的实例

    在某种意义上,你可以说API对它的需求撒谎,因为它声称它可以处理任何IChecksumGenerator,而实际上它只处理一种特定的类型——让我们称它为OneOnlyCheckSumGenerator。虽然我建议重新设计应用程序以遵守LSP,但您也可以更改构造函数签名以诚实地说明要求:

    public class MyValidator
    {
        private readonly OneAndOnlyChecksumGenerator checksumGenerator;
    
        public MyValidator(OneAndOnlyChecksumGenerator checksumGenerator)
        {
            this.checksumGenerator = checksumGenerator; 
        }
    
        // ...
    }
    
    通过使战略成员虚拟化,您可能仍然能够将OneAndOnlyChecksumGenerator变成测试双元组,这样您就可以创建特定于测试的子类。

    在本书中,Mark Seemann将最后一个解决方案称为私生子注入,并将其视为反模式。这主要是因为您仍然依赖于具体的实现。你应该看看这本书了解更多细节

    在这里指定的情况下,在我看来很可能会有其他代码,在其他地方,生成任何正在验证的代码。我称之为造物主。反过来,这个创建者也可能需要一个IChecksumGenerator。在这种情况下,我会让DI容器完全控制依赖关系

    想象一下,您想要为IChecksumGenerator切换到另一个实现。假设我上面所说的是真的,你们需要在两个地方改变这一点和私生子注射;创建者和验证器。让DI容器控制它将意味着它只有一个位置——容器配置


    让DI容器拥有控制权的另一个好处是,通过引入LSP冲突,它减少了将来的更改将MyValidator更紧密地耦合到具体的IChecksumValidator的可能性。

    如果MyValidator只与IChecksumGenerator的一个特定实现一起工作,那么您将如何用测试双精度替换它?在任何情况下,你都会违反LSP…@马克:关于LSP的观点很好。请把它表述为一个答案。但是我没有理解你关于双重测试的观点为什么这不是违反封装?它是公开依赖项,而不是依赖项的实现方式。关于LSP,你是绝对正确的-我只需要传递一个更具体的依赖项
    public class MyValidator
    {
        private readonly OneAndOnlyChecksumGenerator checksumGenerator;
    
        public MyValidator(OneAndOnlyChecksumGenerator checksumGenerator)
        {
            this.checksumGenerator = checksumGenerator; 
        }
    
        // ...
    }