Стиль кода Java - интерфейсы против абстрактных классов - PullRequest
12 голосов
/ 16 февраля 2011

Мой новый сотрудник, который просматривал какой-то код, который я написал, сказал мне, что она не привыкла видеть интерфейсы, используемые непосредственно в коде 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, но если различные Foo имеют совершенно разные внутренние реализации (или нам все равно, как определенный Foo делает Bar,Пока он это делает), есть ли вред в использовании интерфейса непосредственно в коде?Я понимаю, что это, вероятно, в некоторой степени помидор / помидор, но мне любопытно, есть ли преимущество во втором стиле или недостаток в первом, который я упускаю.

Ответы [ 8 ]

15 голосов
/ 16 февраля 2011

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

5 голосов
/ 16 февраля 2011

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

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

4 голосов
/ 16 февраля 2011

Для меня «она не привыкла» - не достаточно веская причина. Попросите ее уточнить это.

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

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

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

3 голосов
/ 16 февраля 2011

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

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

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

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

2 голосов
/ 16 февраля 2011

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

Это причины для первого interfaces стиля:

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

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

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

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

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

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

2 голосов
/ 16 февраля 2011

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

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

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

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

1 голос
/ 16 февраля 2011

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

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

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

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

0 голосов
/ 16 февраля 2011

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

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...