SOLID / TDD鼓励一个接口实现一个实现

时间:2012-08-27 17:03:31

标签: unit-testing design-patterns dependency-injection tdd solid-principles

我无意中听到了以下内容,并被要求确认此声明:

“SOLID / TDD鼓励一个接口实现一个实现,这不是现实世界而且与接口不相符呢?”

我最初同意,因为TDD和DI的所有在线示例都遵循典型的IRepository / MyRepository示例,其中只有一个实现。经过深思熟虑,我不同意。

我要做的是提供它没有的证明,也是一个例子,说明一个界面可以有多个实现的位置,并显示它如何在DI方面工作。

我希望人们可以帮助我。

更新:虽然我理解DI和单元测试的概念,但我想要展示的是如何在生产中实现一个接口的多个类。

UPDATE2:考虑到一个简单的例子,这里是多个实现的可能实现,但它仍然没有真正回答我想要的。如果您的构造函数对ILogger或IDataProvider或ISomething具有单一依赖性,那该怎么办:

public interface ILogger
{
  void Write(string data);
}

public class FileLogger : ILogger
{
  void Write(string data)
  {
    //
  }
}

public class DBLogger : ILogger
{
  void Write(string data)
  {
    //
  }
}

public class EventViewerLogger : ILogger
{
  void Write(string data)
  {
    //
  }
}

public class Calculator
{
    private IEnumberable<ILogger> loggers;

    public Calculator(IEnumberable<ILogger> loggers)
    {
        this.loggers = loggers;
    }

    public int Add(int a, int b)
    {
      var result = a + b;

      foreach(var logger in loggers)
      {
        logger.Write("Result was " + logger);
      }
    }
}

6 个答案:

答案 0 :(得分:4)

不,您可以拥有任意数量的实现。 TDD测试实现,而不是接口。 DI注入实现,而不是接口。 您可以使用接口,以便可以进行多种实现。

答案 1 :(得分:3)

测试时,通常会为接口创建一个模拟器来隔离被测单元。

虽然你没有自己编写模拟,模拟仍然算作接口的实现给我。


我不知道你想要什么。如果你有很多相同的接口实现,这取决于你的问题。最后 - 没关系。界面的优点并不是因为您可以在生产中同时进行多次实施。我们在项目中有很多接口,可能大多数只有一个实现。它仍然值得拥有它,因为:

  • 调用代码不关心您有多少实现 - &gt;它增强了解耦
  • 您今天只有一个实现,将来可能会有另一个实现。 (我知道,考虑到YAGNI,这是一个弱论点。)
  • 测试有模拟实现
  • 更改实施就像创建另一个一样。您有多个实现,但不是同时实现。 =&GT;它增强了可维护性

简而言之:您不需要在生产中使用许多实现来解释接口的使用。

答案 2 :(得分:2)

SOLID不会在接口和类之间提升1-1映射。

接口隔离最接近我们获得的接口。但它声明您应该尽可能创建特定的接口。 IUserRepository可以分解为IUserStorage(CRUD)和IUserQueries(搜索)。 UserRepository可能会从一开始就实现,而你稍后会将其分解。最棒的是,您可以轻松创建缓存实现(装饰器模式),或者使用不同的数据源来创建和读取(CQRS)。

如果你正确地遵循SOLID,你最终会得到一个小的,定义良好的类,其接口易于使用并为其创建不同的实现。

问题在于,在讨论SOLID时,每个人都只是想到了典型的业务逻辑。但是我们都在不同的框架中运行代码,如果实施SOLID,这将大大受益。

我对TDD不是很擅长,所以我不会评论它。

答案 3 :(得分:1)

我同意Stefan的观点。测试可能不在生产中,但它们是您应用程序的第一批消费者。

关于TDD / SOLID产生无意义接口的论点,我将提出一个警告:SOLID鼓励抽象,而不是接口。如果你正在为所有东西创建接口,这对某些开发人员来说似乎非常苛刻。接口应该是系统用户的扩展点。在某些情况下,如果测试是您的主要目标,那么使用虚拟方法的抽象类或非密封类可能更合适。一些开发人员将内部类上的虚方法视为永远不会在其应用程序范围之外使用的东西。这种说法可能有些优点,但总有可能将其扩展到外部消费者 - 将其细分为小块可能会使其扩展并且重构更容易。还有一些事情可以说是不必手动测试应用程序几个小时。这是你的同事应该愿意做出的妥协。

我开发的应用程序很少,核心服务需要在每个实例上实现更多的实现,但我开发的应用程序需要不同的实现来满足特定于硬件的实现,例如< em>网络摄像头, Kinect 。我可能一次只需要一个,但混合/匹配组件的能力很强。

一次关于多个实现,策略插件模式很受欢迎。可以像模块化应用程序一样广泛,完全通过插件组成它的界面,或者是由配置驱动的税收计算。

根据您的示例,日志记录通常是混合实现的一个很好的示例。我经常使用log4net将所有内容记录到文件中,将错误记录到事件日志中,然后崩溃到电子邮件附加程序。

答案 4 :(得分:0)

所以,您正在寻找一个具有多个实现的接口示例,并在生产环境中使用?

JDBC - Oracle,MySql,任何其他数据库都有自己的JDBC接口实现

JPA - 请参阅JPA Implementations - Which one is the best to use?以获取接口的实现列表

或者您正在寻找别的东西吗?

答案 5 :(得分:0)

鲍勃叔叔,坚实的原则的名字(不是发明者)有一个着名的会计系统的例子 您可以在原则,模式和实践手册中找到它。 在这个例子中,他有一个可以根据员工列表生成报告的类。员工可以是按小时付薪的员工,例如每月付薪员工。然而,他们都是员工,因此实现了相同的界面。