为什么在服务和 dao 层中总是有单一的实现接口?

我工作/看到过一些春季休眠的Web应用程序项目,其接口与实际的服务和dao类一样多。

我一直认为这两个是拥有这些单一实现接口的主要原因:

  1. 弹簧可以将实际实现作为给定类中的依赖项(松散耦合)

    public class Person { 
        @Autowired 
        private Address address;
    
        @Autowired 
        private AccountDetail accountDetail;
    
        public Person(Address address, AccountDetail accountDetail) 
        { // constructor
    
  2. 在单元测试时,我可以创建模拟类并单独测试类。

    Address mockedAddress = mock(Address);
    AccountDetail mockedAccountDetail = mock(AccountDetail);
    Person underTestPerson = new Person(mockedAddress, mockedAccountDetail); 
    // unit test follows
    

但是,最近,我意识到:

Spring可以将具体的实现类作为依赖项:

public class Person { 

@Autowired 
private AddressImpl address;

@Autowired 
private AccountDetailImpl accountDetail;

public Person(AddressImpl address, AccountDetailImpl accountDetail) { 
// constructor

像EasyMock这样的模拟框架也可以模拟具体的类。

AddressImpl mockedAddress = mock(AddressImpl);
AccountDetailImpl mockedAccountDetail = mock(AccountDetailImpl);
Person underTestPerson = new Person(mockedAddress, mockedAccountDetail); 
// unit test follows

此外,根据这次讨论,我认为总结是,在单个应用程序中,界面大多被过度使用,可能是出于惯例或习惯。在我们与其他应用程序接口的情况下,它们通常是最有意义的,例如世界各地的许多应用程序使用的slf4j。在单个应用中,类几乎与接口一样具有抽象性。

所以,我的问题是,为什么我们仍然需要接口,然后有单个实现,如*ServiceImpl和*DaoImpl类,并且不必要地增加我们的代码库大小。在嘲笑具体类时,是否有一些我不知道的问题。

每当我和我的队友讨论这个问题时,我得到的唯一答案是,基于接口实现服务和dao类是每个人都遵循的设计 - 他们提到了弹簧最佳实践,OOP,DDD等。但是我仍然不明白在一个孤立的应用程序中拥有这么多接口背后的实际原因。


答案 1

接口还有更多优点 - 如代理 。如果您的类实现了接口,则默认情况下,JDK 动态代理将用于 AOP 。如果直接使用这些实现,您将被迫使用 CGLIB 代理,方法是使 proxy-target-class=true 。这些需要字节码操作,这与JDK代理不同。

请阅读此处以获取有关此内容的更多信息。

阅读另一个讨论,了解使用接口(Java EE或Spring和JPA)的原因是什么,以获取更多信息。


答案 2

这是一个非常有争议的话题。简而言之,没有 - 至少对于您,开发人员。

在EJB2世界中,家庭和远程接口是必须的,并且正是出于@AravindA提到的原因:代理。安全性、远程处理、池化等都可以包装在代理中,并提供严格在标准库中请求的服务(如 )。DynamicProxy

现在我们已经有了 和 ,Spring(如果你愿意的话,可以的话,EJB3)完全能够像框架开发人员一样检测你的类。问题是,他们所做的是一件非常烦人的事情:他们通常要求你添加一个无参数构造函数。—等等,我在这里有参数?—别介意,只需添加构造函数即可。javaassistcglib

因此,接口在这里保持您的理智。不过,这很奇怪,对于具有适当构造函数的类,无参数构造函数对我来说不是有意义的东西,对吧?事实证明(我应该已经读过规范了,我知道)Spring在你的类中创建了一个接口的功能等价物:一个没有(或忽略)状态并且所有方法都被覆盖的实例。所以你有一个“真实”的实例,还有一个“假接口”,假接口的作用是,它为你提供了所有的安全/事务/远程处理魔术。很好,但很难理解,如果你没有把它拆开,看起来像一个错误。

此外,如果你碰巧在你的类中实现了一个接口,(至少是某些版本的)Spring突然决定你只代理这个接口,而应用程序只是没有明显的原因而无法工作。

因此,到目前为止,原因是安全和理智。这是一个很好的做法是有原因的 - 但从你的帖子中,我看到你已经阅读了所有这些。我今天能看到的最重要原因是WTH /分钟指标,特别是如果我们谈论的是你项目的新人。


推荐