20

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

想象一下这种情况。我有一个抽象类,它定义了一些字段和这些字段的验证方法:

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<String> 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() 方法不是为扩展而设计的。但是在这种情况下我该如何设计扩展呢?

4

2 回答 2

17

该规则正在抱怨,因为派生(扩展)类可能会完全替换您提供的功能而不告诉您。这是一个强烈的迹象,表明您尚未完全考虑如何扩展该类型。它希望你做的是这样的事情:

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();
}

请注意,现在有人仍然可以提供他们自己的验证代码,但他们无法替换您预先编写的代码。根据您打算如何使用该validate方法,您也可以将其设为 public final 。

于 2009-03-19T15:23:55.287 回答
1

尽管 Joel Coehoorn 的回答解释了如何克服 OP 发布的具体问题,但我想提出一种方法,可以更广泛地看待“如何设计扩展?” 正如 OP 在他的评论中指出的那样,给定的解决方案不能随着(类)继承深度的增加而很好地扩展。validateTreeEx()此外,由于明显的原因,在基类中预期需要验证可能的子类 ( ) 是有问题的。

建议:在施工时检查植物属性并validate()完全移除(连同可能的设置器;另见http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-邪恶的.html)。原始代码表明这是一个不变量,在每次操作validate()之前必须为真。grow()我怀疑这种设计是故意的。如果没有操作,可以在建造后“破坏”一个工厂,没有必要一遍又一遍地重新检查有效性。

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

如果在适合组合的情况下使用继承,则不必要地暴露实现细节。生成的 API 将您与原始实现联系在一起,永远限制您的类的性能。更严重的是,通过公开内部结构,您可以让客户直接访问它们。

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

于 2014-12-29T11:50:34.457 回答