Подтвердить что ты не робот

Стиль Java-кода - Интерфейсы и абстрактные классы

Мой новый коллега, который просматривал какой-то код, который я написал, сказал мне, что она не привыкла видеть интерфейсы, используемые непосредственно в Java-коде, например:

public interface GeneralFoo { ... }

public class SpecificFoo implements GeneralFoo { ... }

public class UsesFoo {
     GeneralFoo foo = new SpecificFoo();
}

вместо этого, ожидая увидеть

public interface GeneralFoo { ... }

public abstract class AbstractFoo implements GeneralFoo { ... }

public class SpecificFoo extends AbstractFoo { ... }

public class UsesFoo {
     AbstractFoo foo = new SpecificFoo();
}

Я вижу, когда этот шаблон имеет смысл, если все функции SpecificFoos разделяют функции AbstractFoo, но если различные Foos имеют совершенно разные внутренние реализации (или нам все равно, как конкретный Foo делает Bar, если он это делает), есть ли вред в использовании интерфейса непосредственно в коде? Я понимаю, что это, вероятно, томатная/томатная вещь в некоторой степени, но мне любопытно, есть ли преимущество во втором стиле или недостаток первого стиля, который мне не хватает.

4b9b3361

Ответ 1

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

Ответ 2

Нет, она неопытная, не права. Использование интерфейсов является предпочтительным, и запись избыточных абстрактных суперклассов для избыточности является избыточным.

UsesFoo должен заботиться о поведении, указанном интерфейсом, а не о суперклассе его зависимостей.

Ответ 3

Для меня "она не привыкла", это не очень хорошая причина. Попросите ее рассказать об этом.

Лично я использую ваше решение, потому что:

  • AbstractFoo избыточен, а объявления не имеют значения в текущей ситуации.

  • Даже если AbstractFoo был необходим (для некоторых дополнительных функций), я всегда использовал бы наименьший необходимый тип: если GeneralFoo был достаточным, то я использовал бы это, а не какой-то класс, полученный из него.

Ответ 4

Это зависит только от вашей проблемы.

Если вы используете только интерфейсы, то, если все ваши классы имеют один и тот же метод, его нужно будет реализовать избыточно (или перейти в класс Util).

С другой стороны, если вы пишете абстрактный абстрактный класс, вы решили эту проблему, но теперь ваш подкласс не может быть подклассом другого класса из-за отсутствия множественного наследования в Java. Если уже было необходимо расширить какой-то класс, это невозможно.

Итак, скоро - это компромисс. Используйте то, что лучше в вашем конкретном случае.

Ответ 5

В прямом использовании интерфейса в коде нет вреда. Если бы это было так, у Java не было бы интерфейсов.

Недостатки использования интерфейса напрямую включают неспособность достичь и методы класса, которые не реализованы в интерфейсе. Для плохо написанных интерфейсов или классов, которые добавляют много "других" функциональных возможностей, это нежелательно, поскольку вы теряете способность обращаться к необходимым методам. Однако в некоторых случаях это может быть отражением плохого выбора дизайна при создании интерфейса. Без подробностей это слишком сложно узнать.

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

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

Ответ 6

Мне любопытно, есть ли преимущество для второго стиля или недостаток первого стиля, который мне не хватает

Это объясняет первый стиль interfaces:

  • Часто дизайн таков, что интерфейс является публичным интерфейсом концепции, в то время как абстрактный класс является деталью реализации концепции.

    Например, рассмотрим List и AbstractList в рамках коллекции. List действительно то, что клиенты обычно после; меньше людей знают о AbstractList, потому что его деталь реализации помогает поставщикам (исполнителям) интерфейса), а не клиентам (пользователям) этого класса.

  • Интерфейс является более слабым, поэтому он более гибкий, чтобы поддерживать будущие изменения.

  • Используйте ту, которая более понятна, представляет требование класса, которое часто является интерфейсом.

    Например, часто используется List, а не AbsrtactList или ArrayList. Используя интерфейс, будущему разработчику может быть яснее, чтобы этот класс нуждался в некотором List, но ему не требуется, в частности, AbstractList или ArrayList. Если этот класс полагался на некоторое AbstractList -специфическое свойство, то есть ему нужно использовать метод AbstractList, тогда использование AbstractList list = ... вместо List list = ... может быть намеком на то, что этот код основан на чем-то конкретном для AbstractList.

  • Это может упростить тестирование/издевку, чтобы использовать меньший, более абстрактный интерфейс, а не использовать абстрактный класс.

Ответ 7

Некоторые считают, что некорректная реклама объявляет переменные их сигнатурами AbstractFoo, так как класс UsesFoo связан с некоторыми деталями реализации foo.

Это приводит к меньшей гибкости - вы не можете поменять тип выполнения foo на любой класс, реализующий интерфейс GeneralFoo; вы можете вводить только экземпляры, которые реализуют потомок AbstractFoo, оставляя вас с меньшим подмножеством.

В идеале должно быть возможно, чтобы такие классы, как UsesFoo, знали только интерфейсы используемых ими коллаборационистов, а не подробности реализации.

И, конечно, если нет необходимости объявлять что-либо abstract в abstract class AbstractFoo implements GeneralFoo - то есть, нет общей реализации, которую все подклассы будут использовать повторно - тогда это просто пустая трата лишнего файла и уровней в вашем иерархия.

Ответ 8

Во-первых, я использую абстрактные и интерфейсные классы обильно.

Я думаю, вам нужно увидеть ценность использования интерфейса перед его использованием. Я думаю, что подход к проектированию состоит в том, что у нас есть класс, поэтому у нас должен быть абстрактный класс, и поэтому мы должны иметь интерфейсы.

Во-первых, зачем вам нужен интерфейс, во-вторых, почему у вас есть абстрактный класс. Кажется, она может добавлять вещи, чтобы добавить вещи ради. В решении должно быть четкое значение, иначе вы говорите о коде, который не имеет значения.

Императивно там вы должны увидеть значение в своем решении. Если нет никакой ценности, решение ошибочно, если это не объясняется вам, она не понимает, почему она это делает.

Простой код - лучшее решение и рефакторинг, когда вам нужна сложность, гибкость или любая воспринимаемая ценность, которую она получает от решения.

Показать значение или удалить код!

Еще одна вещь - посмотрите на код библиотеки Java. Использует ли это шаблон абстрактного/интерфейса, который она применяет. НЕТ!