我读到了关于Spring如何鼓励您在代码中使用接口的地方。我没有看到它。 spring xml配置中没有接口的概念。 Spring的哪一部分实际上鼓励您使用接口(除了文档)?
答案 0 :(得分:31)
Dependency Inversion Principle很好地解释了这一点。特别是图4。
一个。高级模块不应该依赖于低级模块。两者都应该取决于抽象。
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" />
答案 1 :(得分:28)
为类定义接口时,它有助于依赖注入。您的Spring配置文件本身没有关于接口的任何内容 - 您只需输入类的名称。
但是如果你想注入另一个提供“等效”功能的类,使用界面确实有帮助。
例如,说你有一个分析网站内容的课程,你用Spring注入它。如果您注入的类知道实际的类是什么,那么为了更改它,您将不得不更改大量代码以使用不同的具体类。但是,如果你创建了一个Analyzer
界面,你可以轻松地注入原始的DefaultAnalyzer
,就像模拟DummyAnalyzer
一样,或者甚至是另一个基本相同的东西,例如{ {1}}或其他任何内容。为了使用其中一个,你只需要更改你在Spring配置文件中注入的类名,而不是通过你的代码更改类。
在我真正开始看到有用之前,我花了一个半个项目。像大多数事物(在企业语言中)最终都是有用的,一开始似乎是毫无意义的工作,直到你的项目开始增长,然后你通过做更多的工作预先发现你节省了多少时间。
答案 2 :(得分:10)
这里的大部分答案都是某种形式的“你可以轻松换掉实现”,但我认为他们没有回答的是为什么?部分。对此我认为答案几乎是可测试的。无论您是否使用Spring或任何其他IOC框架,使用依赖注入都可以使您的代码更容易测试。如果是编写器而不是PrinterWriter,您可以在单元测试中模拟Writer接口,并确保您的代码以您期望的方式调用它。如果您直接依赖于类实现,您唯一的选择是走到打印机并检查它,这不是非常自动化。此外,如果你依赖于调用类的结果,不能模拟它可能会阻止你能够到达测试中的所有代码路径,从而降低它们的质量(可能)简单地说,你应该解耦对象从应用程序逻辑创建图形。这样做可以使您的代码更容易测试。
答案 3 :(得分:4)
没有人提到在很多情况下创建一个接口是不必要的,这样就可以快速切换实现类,因为只是不会有多个实现类。
当不需要创建接口时,将通过对创建类(接口和实现),添加不必要的样板接口并创建潜在的依赖混淆,因为在XML配置文件中,组件有时会被其接口引用,有时也会被其引用实现,在运行时没有任何后果,但在代码约定方面却不连贯。
答案 4 :(得分:2)
你可能想尝试自己使用它来更好地看到这一点,从文档中可能不清楚Spring如何鼓励界面使用。
以下是几个例子:
假设您正在编写一个需要从资源(例如文件)中读取的类,该类可以通过多种方式引用(例如,在类路径,绝对文件路径中,作为URL等)。您需要在类上定义org.springframework.core.io.Resource
(接口)属性。然后在Spring配置文件中,您只需选择实际的实现类(例如org.springframework.core.io.ClassPathResource
,org.springframework.core.io.FileSystemResource
,org.springframework.core.io.UrlResource
等)。 Spring基本上是一个非常通用的工厂。
如果你想利用Spring的AOP集成(例如添加事务拦截器),你几乎需要定义接口。您可以在Spring配置文件中定义拦截点,Spring会根据您的界面为您生成代理。
这些是我个人经验丰富的例子。我相信那里还有更多。
答案 5 :(得分:1)
从界面生成代理很容易。
如果您查看任何spring应用程序,您将看到服务和持久性接口。使春天成语确实鼓励使用界面。它没有那么明确。
答案 6 :(得分:1)
如果您不使用接口,则可能会导致自动装配失败: 有时Spring会为Bean创建一个Proxy类。 此Proxy类不是服务实现的子类,但它重新实现了所有接口。 Spring会尝试自动装配这个Bean的实例,但是这个Proxy类与Bean类不兼容。因此,使用Bean类声明字段可能会导致“不安全的字段分配”异常。
你无法合理地知道Spring何时会代理服务(也不应该),所以为了保护自己免受这些意外的影响,最好的办法就是在声明自动装配的字段时声明一个接口并使用这个界面。
答案 7 :(得分:1)
编写单独的接口会增加复杂性和通常不需要的样板代码。这也使调试更加困难,因为当您单击IDE中的方法调用时,它显示的是接口而不是实现。除非您在运行时交换实现,否则就不必走那条路。
Mockito之类的工具可以非常轻松地使用依赖项注入来测试代码,而无需在接口上堆积
。答案 8 :(得分:0)
Spring不会强迫你在任何地方使用接口,这只是一个好习惯。如果你的bean有一些属性是接口而不是具体的类,那么你可以简单地使用实现相同接口的模型切换出一些对象,这对某些测试用例很有用。
如果你使用例如Hibernate支持clases,你可以为你的DAO定义一个接口,然后单独实现它;具有接口的优点是您可以使用Spring拦截器对其进行配置,这将允许您简化代码;您不必编写任何代码,使用HibernateExceptions并在finally段中关闭会话,并且您也不必以编程方式定义任何事务,只需使用Spring以声明方式配置所有这些内容。
当您编写快速且脏的应用程序时,您可以使用JDBC或一些简单的框架来实现一些简单的DAO,这些框架最终不会在最终版本中使用;如果它们实现了一些通用接口,您将能够轻松地切换这些组件。