Jam*_*unn 26 java inheritance abstract-class interface hamcrest
我只是看着GitHub上的Java Hamcrest代码,并注意到他们采用了一种似乎不直观和笨拙的策略,但它让我想知道我是否遗漏了一些东西.
我在HamCrest API中注意到有一个接口Matcher和一个抽象类BaseMatcher.Matcher接口使用此javadoc声明此方法:
/**
* This method simply acts a friendly reminder not to implement Matcher directly and
* instead extend BaseMatcher. It's easy to ignore JavaDoc, but a bit harder to ignore
* compile errors .
*
* @see Matcher for reasons why.
* @see BaseMatcher
* @deprecated to make
*/
@Deprecated
void _dont_implement_Matcher___instead_extend_BaseMatcher_();
Run Code Online (Sandbox Code Playgroud)
然后在BaseMatcher中,此方法实现如下:
/**
* @see Matcher#_dont_implement_Matcher___instead_extend_BaseMatcher_()
*/
@Override
@Deprecated
public final void _dont_implement_Matcher___instead_extend_BaseMatcher_() {
// See Matcher interface for an explanation of this method.
}
Run Code Online (Sandbox Code Playgroud)
不可否认,这既有效又可爱(令人难以置信的尴尬).但是,如果每个实现Matcher的类都是为了扩展BaseMatcher,那么为什么要使用一个接口呢?为什么不首先让Matcher成为一个抽象类,让所有其他匹配器扩展它?以Hamcrest的方式做这件事有什么好处吗?或者这是不良做法的一个很好的例子?
编辑
一些好的答案,但为了寻找更多的细节,我正在提供赏金.我认为向后/二进制兼容性的问题是最好的答案.但是,我希望看到更多的兼容性问题,理想情况下是一些代码示例(最好是Java).另外,"向后"兼容性和"二进制"兼容性之间是否有细微差别?
进一步编辑
2014年1月7日 - pigroxalot在下面提供了一个答案,链接到 HamCrest的作者对Reddit的评论.我鼓励每个人阅读它,如果你发现它提供了信息,请upvote pigroxalot的回答.
即便进一步编辑
2017年12月12日 - pigroxalot的答案以某种方式被删除,不知道是怎么回事.这太糟糕了......简单的链接非常有用.
ajb*_*ajb 10
该git log有这个条目,从12月(初始签入后约9个月)2006年:
添加了所有Matchers应该扩展的抽象BaseMatcher类.随着Matcher接口的发展,这允许未来的API兼容性[原文如此].
我没有试图弄清楚细节.但是,随着系统的发展,保持兼容性和连续性是一个难题.它确实意味着,如果你从头开始设计整个产品,有时你最终会得到一个永远不会创造的设计.
但是,如果每个实现Matcher的类都是为了扩展BaseMatcher,那么为什么要使用一个接口呢?
这不完全是意图.抽象基类和接口从OOP角度提供完全不同的"契约".
一个接口是一个通信协议.接口由类实现,以向世界表明它遵守某些通信标准,并且将响应具有特定参数的特定呼叫给出特定类型的结果.
一个抽象基类是执行合同.抽象基类由类继承,以提供基类所需的功能,但留给实现者提供.
在这种情况下,两者都重叠,但这只是一个方便的问题 - 接口是你需要实现的,而抽象类是为了使接口更容易实现 - 没有任何要求使用该基类是能够提供界面,只是为了减少工作量.您绝不限于为自己的目的扩展基类,不关心接口契约,或实现实现相同接口的自定义类.
在老式的COM/OLE代码和其他框架中,给定的实践实际上相当普遍,这些框架有助于进程间通信(IPC),它将实现与接口分离成为基础 - 这正是这里所做的.