简体   繁体   English

针对接口编程:您是否为所有域类编写接口?

[英]Programming against interfaces: Do you write interfaces for all your domain classes?

I agree, that programming against interfaces is a good practice. 我同意,针对接口的编程是一种很好的做法。 In most cases in Java "interface" in this sense means the language construct interface, so that you write an interface and an implementation class and that you use the interface instead of the implementation class most of the time. 在大多数情况下,Java“接口”在这个意义上意味着语言构造接口,因此您可以编写接口和实现类,并且在大多数情况下使用接口而不是实现类。

I wonder if this is a good practice for writing domain models as well. 我想知道这是否也是编写域模型的好习惯。 So, for example if you've got a domain class Customer and each customer may have a list of Orders, would you generally also write interfaces ICustomer and IOrder. 因此,例如,如果您有一个域类Customer,并且每个客户可能都有一个Orders列表,您通常也会编写接口ICustomer和IOrder。 And also would Customer have a list of IOrders instead of Orders? 客户也会有一份IOrders而不是Orders的列表吗? Or would you use interfaces in the domain model, only if it is really driven by the domain, eg you've got at least two different types of Orders? 或者你会在域模型中使用接口,只有它真的是由域驱动的,例如你至少有两种不同类型的订单? In other words, would you use interfaces because of only technical needs in the domain model, or only when it is really appropriate with respect to the actual domain? 换句话说,您是否会因为域模型中的技术需求而使用接口,或者仅在与实际域相关时才使用接口?

Writing interfaces "just because" strikes me as a waste of time and energy, not to mention a violation of the KISS-principle. 编写接口“只是因为”让我感到浪费时间和精力,更不用说违反了KISS原则。

I write them when they are actually useful in representing common behavior of related classes, not just as a fancy header file. 当它们实际用于表示相关类的常见行为时,我会编写它们,而不仅仅是一个花哨的头文件。

Don't over design your system. 不要过度设计你的系统。 If you find out that you have several types of Orders and think it's appropriate to declare an interface for Orders than refactor it when the need arises. 如果你发现你有几种类型的订单,并认为为Orders声明一个接口是合适的,而不是在需要时重构它。 For domain models, the probability is high that the specific interface will change much over the lifetime of development, so it rarely useful to write an interface early. 对于域模型,特定接口在开发生命周期中会发生很大变化的概率很高,因此很早就编写接口很有用。

Interfaces are an excellent way of isolating components for unit testing purposes and general dependency management. 接口是隔离组件的绝佳方式,用于单元测试和一般依赖关系管理。 Saying that, I often prefer abstract classes so at least some of the common behaviour is offloaded there rather than forcing some of the duplication that interfaces bring. 说,我经常更喜欢抽象类,所以至少有一些常见的行为被卸载,而不是强迫接口带来的一些重复。 Modern IDEs make it quick and easy to generate interfaces so they aren't that much work :-) 现代IDE可以快速轻松地生成界面,因此它们没有那么多工作:-)

No, I only use interfaces on Domain Objects to keep them loosely coupled. 不,我只使用域对象上的接口来保持松散耦合。 For me the main hook of developing my own code with interfaces is that I can easily create mocks when doing Unit Testing. 对我来说,使用接口开发自己的代码的主要方面是我可以在进行单元测试时轻松创建模拟。 I don't see the point in mocking Domain Objects as they don't have the same dependencies that a service layer or DAO layer class would have. 我没有看到模仿域对象的重点,因为它们没有服务层或DAO层类所具有的相同依赖性。

This certainly doesn't mean to stray away from using Interfaces in Domain Objects. 这当然不意味着偏离在域对象中使用接口。 Use where appropriate. 适当时使用。 For example, lately I've been working on a webapp where different types of Domain Objects correspond to permalink pages in which users can leave comments. 例如,最近我一直在开发一个webapp,其中不同类型的域对象对应于用户可以留下评论的永久链接页面。 So, each of these Domain Objects now implement the "Commentable" interface. 因此,每个域对象现在都实现了“可注释”接口。 All of the comment-based code is then programmed to the Commentable interface instead of the Domain Objects. 然后将所有基于注释的代码编程到Commentable接口而不是Domain Objects。

Actually, this question is an example of the common misunderstanding about "programming against interfaces". 实际上,这个问题是关于“对接口编程”的常见误解的一个例子。

You see, this is a very good principle, but it does not mean what many people think it means! 你看,这一个很好的原则,但并不意味着许多人认为这意味着!

"Program to an interface, not an implementation" (from the GoF book) means just that, not that you should go out of your way to create separate interfaces for everything. “编程到一个接口,而不是一个实现”(来自GoF书)意味着,而不是你应该尽力为所有东西创建单独的接口。 If you have an ArrayList object, then declare the variable/field/parameter/return type as being of type List . 如果您有一个ArrayList对象,则将变量/ field / parameter / return类型声明为List类型。 Client code will then only deal with the interface type. 然后客户端代码只处理接口类型。

In the "Effective Java" book from Joshua Bloch, the principle is expressed more clearly, in "Item 52: Refer to objects by their interfaces". 在Joshua Bloch的“Effective Java”一书中,原则在“项目52:通过接口引用对象”中更清楚地表达出来。 It even says, in bold letters: 它甚至用粗体字母说:

It is entirely appropriate to refer to an object by a class rather than an interface if no appropriate interface exists. 如果不存在适当的接口,则通过类而不是接口引用对象是完全合适的。

For unit testing, the situation is entirely dependent on the capabilities of the mocking tool used. 对于单元测试,情况完全取决于所使用的模拟工具的功能。 With my own tool, JMockit , I can write unit tests just as easily for code that uses interfaces and Dependency Injection as for code that uses final classes instantiated from inside the code under test. 使用我自己的工具JMockit ,我可以像使用接口和依赖注入的代码一样轻松编写单元测试,对于使用从测试代码中实例化的最终类的代码。

So, for me, the answer is: always use interfaces that already exist, but avoid creating new ones if there is no good reason to do so (and testability , by itself, should not be one). 因此,对我来说,答案是:始终使用已经存在的接口,但如果没有充分的理由,则避免创建新接口(并且可测试性本身不应该是一个)。

I'd recommend staying lean and agile - don't do anything until you need to do it, then let your IDE do the refactoring for you when you need it. 我建议保持精益和敏捷 - 在你需要之前不要做任何事情,然后让IDE在你需要的时候为你做重构。

Its pretty trivial in IDEA/eclipse to turn a concrete class into an interface when you decide you need one. 它在IDEA / eclipse中非常微不足道,当你决定需要一个时,将一个具体的类变成一个接口。

Then use dependency injection with Spring or Guice if you have many implementations of the interface to inject into the places in your code you're using 'new' 然后使用依赖注入SpringGuice,如果你有很多接口实现注入代码中你正在使用'new'的地方

Writing interface before it gets needed (either for testing or architecture) is an overkill. 在需要之前编写接口(用于测试或架构)是一种矫枉过正。

Additionally, writing an interface manually is a waste of time. 此外,手动编写接口是浪费时间。 You can use Resharper's refactoring "Pull Members" to let it create new interface out of the specific class in a matter of seconds. 您可以使用Resharper的重构“Pull Members”让它在几秒钟内创建特定类的新界面。 Other refactoring tools that integrate with IDE should have similar functionality as well. 与IDE集成的其他重构工具也应具有类似的功能。

I usually only use interfaces where it makes sense on smaller projects. 我通常只使用对较小项目有意义的接口。 However, my latest job has a large project where nearly every domain object has an interface. 但是,我的最新工作有一个大型项目,几乎每个域对象都有一个接口。 It's probably overkill and it's definitely annoying, but the way we do testing and use Spring for dependency injection sort of requires it. 它可能有点过头了,这肯定很烦人,但我们测试和使用Spring进行依赖注入排序的方式需要它。

We mostly write web applications with Wicket, Spring and Hibernate and we use interfaces for Spring Beans, eg Services and DAOs. 我们主要用Wicket,Spring和Hibernate编写Web应用程序,我们使用Spring Beans的接口,例如Services和DAO。 For these classes, interfaces make totally sense. 对于这些类,接口完全有意义。 But we also use interfaces for every single domain class and I think this is just overkill. 但我们也为每个域类使用接口,我认为这只是矫枉过正。

Even if you are pretty sure there is only going to be one concrete type of a model object, using interfaces makes mocking and testing somewhat easier (but these days there are framworks that can help you generate mock classes automatically, even for concrete Java classes- Mockito, JTestR, Spring, Groovy...) 即使您非常确定只有一个具体类型的模型对象,使用接口会使模拟和测试变得更容易(但是现在有一些框架可以帮助您自动生成模拟类,即使对于具体的Java类 - Mockito,JTestR,Spring,Groovy ...)

But I even more often use interfaces for services, since it is even more important to mock away them during testing, and programming against interfaces helps you think about stuff like encapsulation. 但我更经常使用接口来提供服务,因为在测试过程中模拟掉它们更为重要,而对接口进行编程可以帮助你思考像封装这样的东西。

Writing interfaces for domain classes can make sense if you're using it for unit testing. 如果您将域类用于单元测试,那么编写域类接口就有意义了。 We use Mock objects in unit testing. 我们在单元测试中使用Mock对象。 So, if you have an interface for a domain object and your domain object itself is NOT ready but your client can test its use of the interface by the help of mock objects. 因此,如果您有域对象的接口,并且您的域对象本身尚未就绪,但您的客户端可以借助模拟对象测试其对接口的使用。

Intefaces also test multiple implementations of your interfaces for your domain model. Inteface还为您的域模型测试接口的多个实现。 So, I don't think it is always overkill. 所以,我不认为它总是矫枉过正。

I think that the main reason for programming against interfaces is testability. 我认为针对接口编程的主要原因是可测试性。 Hence, for the domain objects - just stick to POJOs or POC#Os :) etc., ie, just keep your classes from adding any specific framework so to prevent them from differend build and runtime dependencies and that's all. 因此,对于域对象 - 只需坚持使用POJO或POC#Os :)等,即只是让您的类不添加任何特定的框架,以防止它们产生不同的构建和运行时依赖性,这就是全部。 Creating interfaces for DAOs is a good idea though. 但是,为DAO创建接口是一个好主意。

We extract interfaces from everything just because it assists in testing (mocking) and for stuff like AOP. 我们从一切中提取接口只是因为它有助于测试(模拟)和AOP之类的东西。 Eclipse can do this automatically: Refactor->Extract Interface. Eclipse可以自动执行此操作:Refactor-> Extract Interface。

And if you need to modify the class later, you can use Refactor->Pull Up... to pull up the methods you need onto the interface. 如果您以后需要修改该类,可以使用Refactor-> Pull Up ...将您需要的方法提取到界面上。

*我这样做是因为我需要它来创建我的域对象的代理。

That's another thing to keep in mind that I've run in to, especially with generated domain and DAO objects. 这是另一件要记住的事情,我已经参与其中,特别是对于生成的域和DAO对象。 A lot of the interfaces are just too specific. 很多接口都太具体了。 Say a lot of domain objects have an ID and a status field, Why don't they share a common interface? 说很多域对象都有ID和状态字段,为什么它们不共享一个公共接口? This has caused me frustration, an unnecessarily flat (inheritance-wise) domain model. 这让我感到沮丧,这是一种不必要的扁平(继承智能)领域模型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM