Howto дизайн для расширения - PullRequest
19 голосов
/ 19 марта 2009

Существует Checkstyle rule 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();
}

У меня также есть подкласс Plant:

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 () не предназначен для расширения. Но как мне разработать расширение в этом случае?

Ответы [ 2 ]

17 голосов
/ 19 марта 2009

Правило жалуется, потому что для производного (расширяющего) класса возможно полностью заменить предоставленную вами функциональность, не сообщая вам об этом. Это явный признак того, что вы еще не полностью продумали, как можно расширить тип. Вместо этого он хочет, чтобы вы сделали что-то вроде этого:

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, вы также можете сделать его общедоступным окончательным.

1 голос
/ 29 декабря 2014

Хотя в ответе Джоэла Кохорна объясняется, как преодолеть конкретную проблему, опубликованную ОП, я хотел бы предложить подход, который дает более широкий взгляд на «как проектировать для расширения?» Как указывает ОП в одном из своих комментариев, данное решение плохо масштабируется с растущей (классовой) глубиной наследования. Кроме того, предвидеть в базовом классе необходимость проверки возможных дочерних классов (validateTreeEx()) проблематично по очевидным причинам.

Предложение: Проверьте свойства растений во время строительства и удалите validate() в целом (вместе с возможными установщиками; см. Также http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html). Оригинальный код предполагает, что validate() является инвариантом, который должен быть истинным до каждая операция grow(). Я сомневаюсь, что эта конструкция является преднамеренной. Если нет операции, которая может «сломать» завод после строительства, нет необходимости повторно проверять действительность снова и снова.

Если пойти еще дальше, я бы усомнился в правильности первоначального проекта наследования. Без дополнительных (возможно, полиморфных) операций Tree просто повторно использует некоторые свойства Plant. Я твердо придерживаюсь мнения, что наследование классов не должно использоваться для повторного использования кода. Джош Блох говорит об этом (из Effective Java, 2nd Edition, глава 4):

Если вы используете наследование там, где подходит композиция, вы без необходимости выставлять детали реализации. Полученный API связывает вас к первоначальной реализации, навсегда ограничивая производительность твой класс. Более серьезно, выставляя внутренние органы, вы позволяете клиент получает к ним доступ напрямую.

Также ознакомьтесь с «Пунктом 17: Дизайн и документ для наследования или иначе запретите его» (также глава 4 для той же книги)

...