继承,组合和默认方法 [英] Inheritance, composition and default methods
问题描述
通常承认,通过继承扩展接口的实现不是最佳实践,并且该组合(例如,从头开始再次实现接口)更加可维护。
It is usually admitted that extending implementations of an interface through inheritance is not best practice, and that composition (eg. implementing the interface again from scratch) is more maintenable.
这是有效的,因为接口契约会强制用户实现所有所需的功能。但是在java 8中,默认方法提供了一些可以手动覆盖的默认行为。请考虑以下示例:我想设计一个用户数据库,该数据库必须具有List的功能。出于效率目的,我选择通过ArrayList来支持它。
This works because the interface contract forces the user to implement all the desired functionality. However in java 8, default methods provide some default behavior which can be "manually" overriden. Consider the following example : I want to design a user database, which must have the functionalities of a List. I choose, for efficiency purposes, to back it by an ArrayList.
public class UserDatabase extends ArrayList<User>{}
这通常不被认为是一种很好的做法,如果实际上需要List的全部功能并遵循通常的遗传构成的座右铭,那就更愿意了:
This would not usually be considered great practice, and one would prefer, if actually desiring the full capabilities of a List and following the usual "composition over inheritance" motto :
public class UserDatabase implements List<User>{
//implementation here, using an ArrayList type field, or decorator pattern, etc.
}
但是,如果不注意,一些方法,如spliterator()不需要重写,因为它们是List接口的默认方法。问题是,List的spliterator()方法执行得比ArrayList的spliterator()方法差得多,后者已针对ArrayList的特定结构进行了优化。
However, if not paying attention, some methods, such as spliterator() will not be required to be overridden, as they are default methods of the List interface. The catch is, that the spliterator() method of List performs far worse than the spliterator() method of ArrayList, which has been optimised for the particular structure of an ArrayList.
这迫使开发人员
- 请注意ArrayList有自己的,更有效地实现spliterator(),并手动覆盖他自己的List实现的spliterator()方法或
- 使用默认方法失去了大量的性能。
所以问题是:在这种情况下,人们应该更喜欢构成而非继承吗?
So the question is : is it still "as true" that one should prefer composition over inheritance in such situations ?
推荐答案
在开始考虑性能之前,我们总是应该考虑正确性,即在你的问题中我们应该考虑使用继承而不是代表团暗示。 此EclipseLink / JPA问题已经说明了这一点。由于继承,如果尚未填充延迟填充列表,则排序(同样适用于流操作)不起作用。
Before start thinking about performance, we always should think about correctness, i.e. in your question we should consider what using inheritance instead of delegation implies. This is already illustrated by this EclipseLink/ JPA issue. Due to the inheritance, sorting (same applies to stream operation) don’t work if the lazily populated list hasn’t populated yet.
所以我们必须在两者之间进行权衡专有化(覆盖新的默认
方法)在继承情况下完全破坏的可能性以及默认
的可能性方法不适用于委托案例中的最大性能。我认为答案应该是显而易见的。
So we have to trade off between the possibility that the specializations, overriding the new default
methods, break completely in the inheritance case and the possibility that the default
methods don’t work with the maximum performance in the delegation case. I think, the answer should be obvious.
因为你的问题是关于新的默认
方法改变的情况,应该强调的是,与以前甚至不存在的东西相比,你所说的是性能下降。让我们留在 sort
示例。如果您使用委托并且不覆盖默认
排序方法,则默认
方法的性能可能低于优化 ArrayList.sort
方法,但在Java 8之前,后者不存在,并且未针对 ArrayList
进行优化的算法是标准行为。
Since your question is about whether the new default
methods change the situation, it should be emphasized that you are talking about a performance degradation compared to something which did not even exist before. Let’s stay at the sort
example. If you use delegation and don’t override the default
sorting method, the default
method might have lesser performance than the optimized ArrayList.sort
method, but before Java 8 the latter did not exist and an algorithm not optimized for ArrayList
was the standard behavior.
因此,在Java 8下,您不是失去性能,您只需当你没有覆盖默认
方法时,没有获得更多。我认为,由于其他改进,性能仍然优于Java 7(没有默认
方法)。
So you are not loosing performance with the delegation under Java 8, you are simply not gaining more, when you don’t override the default
method. Due to other improvements, I suppose, that the performance will still be better than under Java 7 (without default
methods).
Stream
API不容易比较,因为在Java 8之前不存在API。但是,很明显类似的操作,例如如果你手动实施减少,除了通过你的代表团名单的 Iterator
之外别无选择,必须防范 remove()
尝试,因此包装 ArrayList
Iterator
,或使用大小()
和 get(int)
委托给支持 List
。因此,没有任何情况下,前$ 默认
方法更好的性能API,因为过去没有 ArrayList
- 特定优化。
The Stream
API is not easily comparable as the API didn’t exist before Java 8. However, it’s clear that similar operations, e.g. if you implement a reduction by hand, had no other choice than going through the Iterator
of your delegation list which had to be guarded against remove()
attempts, hence wrap the ArrayList
Iterator
, or to use size()
and get(int)
which delegate to the backing List
. So there is no scenario where a pre- default
method API could exhibit better performance than the default
methods of the Java 8 API, as there was no ArrayList
-specific optimization in the past anyway.
那就是说,你的API设计可以通过以不同方式使用组合进行改进:不允许 UserDatabase
实施列表<用户>
根本。只需通过访问器方法提供列表
。然后,其他代码不会尝试流式传输 UserDatabase
实例,而是覆盖访问器方法返回的列表。返回的列表可能是只读包装器,它提供了JRE本身提供的最佳性能,并在可行的情况下小心覆盖默认
方法。
That said, your API design could be improved by using composition in a different way: by not letting UserDatabase
implement List<User>
at all. Just offer the List
via an accessor method. Then, other code won’t try to stream over the UserDatabase
instance but over the list returned by the accessor method. The returned list may be a read only wrapper which provides optimal performance as it is provided by the JRE itself and takes care to override the default
methods where feasible.
这篇关于继承,组合和默认方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!