1

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

“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);
      }
    }
}
4

6 回答 6

4

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

于 2012-08-27T17:06:09.017 回答
3

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

尽管您没有自己编写模拟,但模拟对我来说仍然算作接口的实现


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

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

简而言之:您不需要在生产中进行很多实现来原谅使用接口。

于 2012-08-27T17:06:37.023 回答
2

SOLID 不促进接口和类之间的 1-1 映射。

接口隔离最接近我们得到的接口。但它指出您应该创建尽可能具体的接口。AIUserRepository可以分解为IUserStorage(CRUD)和IUserQueries(搜索)。UserRepository可能会从一开始就实现两者,而您稍后将它们分解。这样做的好处是您可以轻松创建缓存实现(装饰器模式)或使用不同的数据源进行写入和读取(CQRS)。

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

问题是大家在讨论 SOLID 的时候只是想到了典型的业务逻辑。但是我们都在不同的框架中运行代码,如果应用 SOLID,这将受益匪浅。

我不太擅长TDD,所以我不会对此发表评论。

于 2012-08-27T18:21:51.227 回答
1

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

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

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

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

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

于 2012-08-27T20:51:36.687 回答
0

Bob 大叔,坚实原则的命名者(不是发明者)有一个著名的会计系统示例,您可以在《原则、模式和实践》一书中找到它。在此示例中,他有一个可以根据员工列表生成报告的类。雇员可以是按小时付费的雇员,或者例如是按月付费的雇员。尽管如此,他们都是雇员,因此实现了相同的接口。

于 2012-09-10T17:09:45.867 回答
0

因此,您正在寻找具有多种实现并用于生产环境的接口示例?

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

JPA - 请参阅JPA 实现 - 最好使用哪一个?接口的实现列表

还是您在寻找其他东西?

于 2012-08-27T17:54:03.323 回答