【问题标题】:spring and interfaces弹簧和接口
【发布时间】:2010-09-20 08:28:28
【问题描述】:

我到处阅读有关 Spring 如何鼓励您在代码中使用接口的信息。我没看到。 spring xml 配置中没有接口的概念。 Spring 的哪一部分实际上鼓励您使用接口(除了文档)?

【问题讨论】:

  • 除了关于 DI 提出的观点之外,像 Spring 远程处理这样的东西完全依赖于使用接口。
  • 这是一个很好的问题。从目前的答案中可以看出,没有人给出具体的答案。

标签: java spring


【解决方案1】:

Dependency Inversion Principle 很好地解释了这一点。特别是图 4。

A.高级模块不应该依赖于低级模块。两者都应该依赖于抽象。

B.抽象不应该依赖于细节。细节应该取决于抽象。

将上面链接中的例子翻译成java:

public class Copy {
    private Keyboard keyboard = new Keyboard(); // concrete dependency
    private Printer printer = new Printer();    // concrete dependency
    public void copy() {
        for (int c = keyboard.read(); c != KeyBoard.EOF) {
            printer.print(c);
        }
    }
}

现在有依赖倒置:

public class Copy {
     private Reader reader; // any dependency satisfying the reader interface will work
     private Writer writer; // any dependency satisfying the writer interface will work
     public void copy() {
        for (int c = reader.read(); c != Reader.EOF) {
            writer.write(c);
        }
     }
     public Copy(Reader reader, Writer writer) {
         this.reader = reader;
         this.writer = writer;
     }
}

现在Copy 支持的不仅仅是从键盘复制到打印机。

它能够从任何Reader 复制到任何Writer,而无需对其代码进行任何修改。

现在有了 Spring:

<bean id="copy" class="Copy">
    <constructor-arg ref="reader" />
    <constructor-arg ref="writer" />
</bean>

<bean id="reader" class="KeyboardReader" />
<bean id="writer" class="PrinterWriter" />

或许:

<bean id="reader" class="RemoteDeviceReader" />
<bean id="writer" class="DatabaseWriter" />

【讨论】:

    【解决方案2】:

    编写单独的接口会增加通常不必要的复杂性和样板代码。它还使调试变得更加困难,因为当您在 IDE 中单击方法调用时,它会显示接口而不是实现。除非您在运行时交换实现,否则没有必要走这条路。

    像 Mockito 这样的工具可以很容易地使用依赖注入来测试代码,而无需在接口上堆积。

    【讨论】:

      【解决方案3】:

      如果您不使用接口,您将面临自动装配失败的风险: 有时 Spring 会为 Bean 创建一个代理类。 此代理类不是服务实现的子类,但它重新实现了它的所有接口。 Spring 将尝试自动装配此 Bean 的实例,但是此 Proxy 类与 Bean 类不兼容。所以用 Bean 类声明一个字段会导致“不安全的字段赋值”异常。

      您无法合理地知道 Spring 何时代理服务(您也不应该),因此为了保护自己免受这些意外的影响,您最好的做法是声明一个接口并在声明自动装配字段时使用此接口。

      【讨论】:

        【解决方案4】:

        目前还没有人提到,在很多情况下不需要创建一个接口,以便实现类可以快速切换,因为实现类不会超过一个。

        当不需要创建接口时,类将成对创建(接口加实现),添加不必要的样板接口并造成潜在的依赖混淆,因为在 XML 配置文件中,组件有时会由其接口引用,有时由其接口引用实现,在运行时没有任何后果,但在代码约定方面不连贯。

        【讨论】:

          【解决方案5】:

          当你为你的类定义一个接口时,它有助于依赖注入。您的 Spring 配置文件本身没有任何关于接口的内容——您只需输入类的名称即可。

          但如果你想注入另一个提供“等效”功能的类,使用接口确实很有帮助。

          例如,假设您有一个分析网站内容的类,并且您正在使用 Spring 注入它。如果您将其注入的类知道实际的类是什么,那么为了将其更改,您将不得不更改大量代码以使用不同的具体类。但是,如果您创建了一个Analyzer 接口,您可以轻松地注入您原来的DefaultAnalyzer,就像您可以模拟一个DummyAnalyzer 或什至另一个基本上做同样事情的接口,如PageByPageAnalyzer 或其他任何东西。为了使用其中之一,您只需更改您在 Spring 配置文件中注入的类名,而不是通过代码更改类。

          我花了一个半的​​时间才真正开始看到它的用处。就像大多数最终有用的东西(在企业语言中)一样,起初它似乎是毫无意义的工作,直到您的项目开始增长,然后您发现通过预先做更多的工作节省了多少时间。

          【讨论】:

          • 虽然你说的是对的,但它不是以弹簧为中心的。它以 java 为中心。
          【解决方案6】:

          从接口生成代理很容易。

          如果您查看任何 Spring 应用程序,您会看到服务和持久性接口。使 spring 成语确实鼓励使用接口。没有比这更明确的了。

          【讨论】:

            【解决方案7】:

            Spring 不会强迫您在任何地方使用接口,它只是一种很好的做法。如果你的 bean 有一些属性是接口而不是具体的类,那么你可以简单地用实现相同接口的模型切换一些对象,这对于某些测试用例很有用。

            如果您使用例如 Hibernate 支持类,您可以为您的 DAO 定义一个接口,然后单独实现它;拥有接口的优点是您将能够使用 Spring 拦截器对其进行配置,这将允许您简化代码;您不必编写任何代码来覆盖 HibernateExceptions 并在 finally 段中关闭会话,也不必以编程方式定义任何事务,您只需使用 Spring 以声明方式配置所有这些内容。

            当您编写快速而肮脏的应用程序时,您可以使用 JDBC 或一些您最终不会在最终版本中使用的简单框架来实现一些简单的 DAO;如果它们实现了一些通用接口,您将能够轻松地切换这些组件。

            【讨论】:

              【解决方案8】:

              这里的大多数答案都是某种形式的“您可以轻松换出实现”,但我认为他们无法回答的是为什么?部分。对此,我认为答案几乎是明确的可测试性。无论您是否使用 Spring 或任何其他 IOC 框架,使用 Dependency Injection 都会使您的代码更易于测试。在使用 writer 而不是 PrinterWriter 的情况下,您可以在单元测试中模拟 Writer 接口,并确保您的代码以您期望的方式调用它。如果你直接依赖于类的实现,你唯一的选择就是走到打印机前检查它,这不是很自动化。此外,如果您依赖于类调用的结果,则无法模拟它可能会阻止您访问测试中的所有代码路径,从而降低它们的质量(可能)简单地说,您应该解耦 Object从应用程序逻辑创建图形。这样做会使您的代码更容易测试。

              【讨论】:

              • 我没有看到任何证据支持单元测试提高软件质量/可靠性/速度的说法。
              • 我更愿意使用 Mockito 而不是编写和维护一堆接口。
              • @LokeshaS Mockito 为我的课程创建模拟实现。我通常不需要创建单独的接口,但 Mockito 也可以模拟这些。
              • @mrog,它确实为类创建了模拟,但问题是我们为什么需要接口。答案是防止层之间的紧密耦合,类之间的紧密耦合(一个服务可以使用其他服务)和最后将实现与抽象分开。通常的论点是我们从来没有第二个实现,那么为什么我们需要接口。我的回答是我们确实以存根/模拟的形式进行了第二次实现。请记住,编写可测试的代码是软件开发的方面之一。所以说从来没有第二次实现是不正确的。
              • @LokeshaS 当然,模拟使用接口,但它不是显式接口,它是每个类自动包含的隐式接口。我的观点是 Mockito 可以完美地工作,无需为每个类编写单独的接口。
              【解决方案9】:

              您可能希望自己尝试使用它以便更好地看到这一点,从文档中可能不清楚 Spring 如何鼓励接口使用。

              这里有几个例子:

              1. 假设您正在编写一个类,该类需要从可能以多种方式引用的资源(例如文件)中读取(例如,在类路径中、绝对文件路径、作为 URL 等)。你想在你的类上定义一个org.springframework.core.io.Resource(接口)属性。然后在您的 Spring 配置文件中,您只需选择实际的实现类(例如,org.springframework.core.io.ClassPathResourceorg.springframework.core.io.FileSystemResourceorg.springframework.core.io.UrlResource 等)。 Spring 基本上是作为一个非常通用的工厂运行的。

              2. 如果您想利用 Spring 的 AOP 集成(例如添加事务拦截器),您几乎需要定义接口。您在 Spring 配置文件中定义拦截点,Spring 会根据您的接口为您生成代理。

              这些都是我亲身经历过的例子。我敢肯定还有更多。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-09-08
                • 2011-05-19
                • 2015-11-27
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多