如何设计扩展

有一个Checkstyle规则DesignForExtension 。 它说:如果你有一个公共/受保护的方法,它不是抽象的,也不是最终的也不是空的,它不是“为扩展而设计的”。 请在Checkstyle页面上阅读此规则的说明以获取基本原理。

想象一下这个案子。 我有一个抽象类,它定义了一些字段和这些字段的validation方法:

public abstract class Plant { private String roots; private String trunk; // setters go here protected void validate() { if (roots == null) throw new IllegalArgumentException("No roots!"); if (trunk == null) throw new IllegalArgumentException("No trunk!"); } public abstract void grow(); } 

我还有一个植物的子类:

 public class Tree extends Plant { private List leaves; // setters go here @Overrides protected void validate() { super.validate(); if (leaves == null) throw new IllegalArgumentException("No leaves!"); } public void grow() { validate(); // grow process } } 

遵循Checkstyle规则,Plant.validate()方法不是为扩展而设计的。 但是在这种情况下如何设计扩展?

该规则正在抱怨,因为派生(扩展)类可以完全替换您提供的function而无需告诉您。 这是一个强有力的迹象,表明您还没有充分考虑如何扩展类型。 你想要你做的是这样的事情:

 public abstract class Plant { private String roots; private String trunk; // setters go here private void validate() { if (roots == null) throw new IllegalArgumentException("No roots!"); if (trunk == null) throw new IllegalArgumentException("No trunk!"); validateEx(); } protected void validateEx() { } public abstract void grow(); } 

请注意,现在有人仍然可以提供自己的validation代码,但它们无法替换您预先编写的代码。 根据您使用validate方法的意图,您也可以将其设为公共最终版。

虽然Joel Coehoorn的答案解释了如何克服OP发布的具体问题,但我想提出一种方法,它更广泛地看待“如何设计扩展?” 正如OP在他的一条评论中指出的那样,给定的解决方案不能很好地扩展(类)inheritance深度。 此外,由于显而易见的原因,在基类中预期validation可能的子类( validateTreeEx() )的需要是有问题的。

建议:在施工时检查工厂属性并完全删除validate() (以及可能的setter;另请参阅http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods -are-evil.html )。 原始代码表明validate()是一个不变量,在每次grow()操作之前必须为true。 我怀疑这个设计是故意的。 如果没有可以在施工后“破坏”工厂的操作,则无需一遍又一遍地重新检查有效性。

更进一步,我会质疑初始inheritance设计的健全性。 没有额外的(可能是多态的)操作, Tree只是重用了Plant的一些属性。 我坚持认为,类inheritance不应该用于代码重用。 Josh Bloch有这个说法(来自Effective Java,第2版,第4章):

如果在合成适当的地方使用inheritance,则会不必要地公开实现细节。 生成的API将您与原始实现联系起来,永远限制了您的类的性能。 更严重的是,通过公开内部,您可以让客户端直接访问它们。

另请查看“项目17:inheritance的设计和文档,否则禁止它”(同一本书的第4章)