Разве неправильно сделать каждый реальный класс для наследования интерфейсу?

Это в ответ на определенные комментарии, сделанные Zed Shaw в его блоге давным-давно.

Эксперты будут затем прогуливаться прочь для реализации их Пылающей Башни Столпотворения без любых комментариев, ужасно сложная насмешка включила тесты, удостоверившись, что КАЖДЫЙ КЛАСС ИМЕЕТ ИНТЕРФЕЙС AN и окончание каждого класса с “Impl”, потому что, ну, в общем, это - лучшая практика.

Я использую Spring и Google Guice в равных мерах, и я заметил, что эти платформы действительно используют постфикс Impl, но экономно. В моем коде я использую интерфейсы везде, потому что мне сказали, что он помогает дразнить и т.д., у меня есть наивное понимание проблемы? (Возможно, ложные платформы могут работать с абстрактными классами или классами, я не знаю. Я никогда не пробовал) Для моих конкретных реализаций, я выбрал конвенцию Spring добавления префикса названия реализации со словом Значение по умолчанию.

e.g. BottleOpener (interface) is implemented by DefaultBottleOpener (class)

Какова лучшая практика по этой проблеме?

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

10
задан Jacques René Mesrine 19 February 2010 в 10:44
поделиться

13 ответов

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

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

Интерфейсы отделяют API от способа их реализации. Если ваша реализация не изменится, интерфейс не имеет особого смысла.

Зед Шоу - потрясающий писатель и разработчик, но относитесь к его словам с недоверием. Я думаю, что некоторые из чрезмерных преувеличений, которыми он балуется, связаны с развлекательной ценностью. Он прав («Не делайте вещей только потому, что кто-то, претендующий на авторитет, говорит вам, что это« лучшая практика »»), но то, как он это говорит, - отчасти театр.

17
ответ дан 3 December 2019 в 13:54
поделиться

Выход за борт интерфейсов - вполне реальная возможность. Игнорирование [отрицательного] штрафа в производительности при выполнении каждого вызова метода с использованием виртуальной диспетчеризации также вводит в систему больше опций. Это может затруднить отладку и защиту программного обеспечения. Если ваш метод/функция принимает какую-либо старую реализацию интерфейса, вы приняли решение принять, что реализация могла реализовать интерфейс неправильно или, возможно, даже злонамеренно. То, как ваша программа/библиотека/инфраструктура допускает вариации, является важной частью процесса проектирования.

-121--3039978-

Параметр PREPEND _ WWW делает именно это.

-121--3438918-

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

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

0
ответ дан 3 December 2019 в 13:54
поделиться

Думаю, это зависит от вашего дизайна. Если вы хотите использовать внедрение зависимостей или тестирование имитационных объектов, то интерфейсы имеют смысл. Кроме того, если вы планируете иметь несколько реализаций, то интерфейсы определенно необходимы для определения контракта на поведение.

Если сомневаетесь, возможно, примените гибкий подход?

Просто понадобится открывалка для пивных бутылок

class BeerBottleOpener // life's good

Теперь понадобится открывалка для бутылок с вином

class WineBottleOpener // life's still good

Немного отрефакторинг

interface BottleOpener;
class BeerBottleOpener implements BottleOpener;
class WineBottleOpener implements BottleOpener;
// now living the life!  ;-)

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

1
ответ дан 3 December 2019 в 13:54
поделиться

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

Я не вижу необходимости создавать интерфейс, чтобы отличать API от реализации. Список публичных функций класса с их подписями - это API. Если классу требуются функции worker-bee, которые не являются частью API, они не должны быть общедоступными. Я не думаю, что

я создаю интерфейс, когда в данном контексте он служит полезной цели. В основном это означает:

(a) На практике моей наиболее частой причиной является реализация / имитация множественного наследования. Если мне нужно A extends B, но мне также нужна функция, которая принимает C, и я хочу передать A этой функции, тогда я должен сделать интерфейс B или C интерфейсом, а не родительский класс, и сделать его A extends B реализует C или наоборот.

(b) Когда я создаю API для библиотеки или служебной функции, которая будет реализована чем-то вне этой библиотеки или служебной программы, обычно несколькими объектами. Примером этого может служить интерфейс «Comparable» в библиотеке Java.Утилите сортировки нужна функция для сравнения пары объектов. Когда программа вызывает утилиту сортировки с объектами, определенными в вызывающей программе, нельзя ожидать, что утилита сортировки знает, как их сравнивать, поэтому вызывающая программа должна предоставлять функцию сравнения. Подпись для функции сравнения логически входит в интерфейс, который вызывающий объект может реализовать для своих объектов.

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

2
ответ дан 3 December 2019 в 13:54
поделиться

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

6
ответ дан 3 December 2019 в 13:54
поделиться

Лучшая практика:

  • Выберите соглашение и будьте последовательны в нем; и
  • Не переусердствуйте, заставляя все реализовывать интерфейс.
6
ответ дан 3 December 2019 в 13:54
поделиться

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

1
ответ дан 3 December 2019 в 13:54
поделиться

Делать что-либо до одержимости - неправильно. Это всего лишь инструменты, а мы - мастера.

0
ответ дан 3 December 2019 в 13:54
поделиться

Превышение интерфейсов - вполне реальная возможность. Игнорирование [незначительного] снижения производительности при вызове каждого метода с использованием виртуальной диспетчеризации также вводит дополнительные параметры в систему. На самом деле это может затруднить отладку и защиту программного обеспечения. Если ваш метод / функция принимает любой старый разработчик интерфейса, вы приняли решение признать, что разработчик мог реализовать интерфейс неправильно или, возможно, даже злонамеренно. То, как ваша программа / библиотека / фреймворк допускает вариации, является важной частью процесса проектирования.

0
ответ дан 3 December 2019 в 13:54
поделиться

Наличие интерфейсов на объектах и ​​возможность взаимодействия объектов друг с другом только через интерфейсы - это хорошо.

Обратите внимание, что я имею в виду объекты, а не коллекции или другие вещи, которые по сути являются «данными».

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

(править)

Это действительно о разнице между косвенным обращением и абстракцией. Интерфейс, который напрямую соответствует вашему API, - это просто косвенное обращение. Интерфейс, который напрямую соответствует потребностям потребителя, является абстракцией, поскольку он объявляет то, потребитель хочет сделать, и скрывает всю информацию о том, как это делается.

У Зеда Шоу есть хорошая статья по этому поводу.

http://www.zedshaw.com/essays/indirection_is_not_abstraction.html

1
ответ дан 3 December 2019 в 13:54
поделиться

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

Есть случаи, когда это излишне, но это не стоит вам многого.

Можно даже привести доводы в пользу того, чтобы заставить Domain Objects реализовывать интерфейсы. Преимущества:

  • Вы можете скрыть уродливые детали от клиентского кода (например, JPA аннотации, сеттеры, которые будут использоваться только только ORM-фреймворком)
  • Так же, как с DAO или сервисами, вы можете программировать против абстракций. Подумайте о пользовательской абстракция - ваша бизнес-логика не зависит от того, как и где они хранятся (например, в БД или ОС), и вы можете легко использовать адаптер если вам приходится работать с сторонним кодом.
1
ответ дан 3 December 2019 в 13:54
поделиться

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

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

2
ответ дан 3 December 2019 в 13:54
поделиться

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

Я видел код, в котором было 400 классов и 400+ интерфейсов для этих классов. Одного копания в этой куче кода с его плохими соглашениями об именах было достаточно, чтобы я вздрогнул. С другой стороны, я имею дело со сторонней проприетарной библиотекой без интерфейсов. Издевательство над этими объектами превращается в мучение.

2
ответ дан 3 December 2019 в 13:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: