Это в ответ на определенные комментарии, сделанные Zed Shaw в его блоге давным-давно.
Эксперты будут затем прогуливаться прочь для реализации их Пылающей Башни Столпотворения без любых комментариев, ужасно сложная насмешка включила тесты, удостоверившись, что КАЖДЫЙ КЛАСС ИМЕЕТ ИНТЕРФЕЙС AN и окончание каждого класса с “Impl”, потому что, ну, в общем, это - лучшая практика.
Я использую Spring и Google Guice в равных мерах, и я заметил, что эти платформы действительно используют постфикс Impl, но экономно. В моем коде я использую интерфейсы везде, потому что мне сказали, что он помогает дразнить и т.д., у меня есть наивное понимание проблемы? (Возможно, ложные платформы могут работать с абстрактными классами или классами, я не знаю. Я никогда не пробовал) Для моих конкретных реализаций, я выбрал конвенцию Spring добавления префикса названия реализации со словом Значение по умолчанию.
e.g. BottleOpener (interface) is implemented by DefaultBottleOpener (class)
Какова лучшая практика по этой проблеме?
UPDATE1 я нахожу полезным возвратить интерфейс из метода, потому что я могу всегда возвращать анонимный класс.
Это больше, чем просто издевательство. В случае Spring это динамические прокси и аспектно-ориентированное программирование. Вот как Spring выполняет транзакции, удаленное взаимодействие и аспекты всех типов.
Я использую интерфейсы для репозиториев, служб и т.п., но я не добавляю интерфейсы к объектам модели или чему-либо еще, реализация которых вряд ли изменится.
Интерфейсы отделяют API от способа их реализации. Если ваша реализация не изменится, интерфейс не имеет особого смысла.
Зед Шоу - потрясающий писатель и разработчик, но относитесь к его словам с недоверием. Я думаю, что некоторые из чрезмерных преувеличений, которыми он балуется, связаны с развлекательной ценностью. Он прав («Не делайте вещей только потому, что кто-то, претендующий на авторитет, говорит вам, что это« лучшая практика »»), но то, как он это говорит, - отчасти театр.
Выход за борт интерфейсов - вполне реальная возможность. Игнорирование [отрицательного] штрафа в производительности при выполнении каждого вызова метода с использованием виртуальной диспетчеризации также вводит в систему больше опций. Это может затруднить отладку и защиту программного обеспечения. Если ваш метод/функция принимает какую-либо старую реализацию интерфейса, вы приняли решение принять, что реализация могла реализовать интерфейс неправильно или, возможно, даже злонамеренно. То, как ваша программа/библиотека/инфраструктура допускает вариации, является важной частью процесса проектирования.
-121--3039978-Параметр PREPEND _ WWW делает именно это.
-121--3438918-Предоставление всего интерфейса (или, как правило, проектирование для гибкости, которая, вероятно, останется неиспользованной) называется сверхинженерией. Если вы, скорее всего, никогда не будете иметь более чем одну конкретную реализацию чего-то, интерфейс просто вздуть. Код, который позволяет избежать создания неиспользуемой гибкости, легче понять, потому что без интерфейса, скрывающего тот факт, что у вас есть только одна реализация, вы знаете , с каким конкретным типом вы имеете дело, и код легче обдумать.
Это также один из моих любимых аргументов против насмешек: он вводит искусственные требования в ваш код, такие как возможность иметь более одной реализации вещей, которые вы будете иметь только одну "реальную" реализацию.
Думаю, это зависит от вашего дизайна. Если вы хотите использовать внедрение зависимостей или тестирование имитационных объектов, то интерфейсы имеют смысл. Кроме того, если вы планируете иметь несколько реализаций, то интерфейсы определенно необходимы для определения контракта на поведение.
Если сомневаетесь, возможно, примените гибкий подход?
Просто понадобится открывалка для пивных бутылок
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 , который может имитировать классы и является более явным и немного более простым.
Я не вижу никаких преимуществ в создании интерфейса для каждого класса. Это просто заставляет вас писать кучу кода дважды.
Я не вижу необходимости создавать интерфейс, чтобы отличать API от реализации. Список публичных функций класса с их подписями - это API. Если классу требуются функции worker-bee, которые не являются частью API, они не должны быть общедоступными. Я не думаю, что
я создаю интерфейс, когда в данном контексте он служит полезной цели. В основном это означает:
(a) На практике моей наиболее частой причиной является реализация / имитация множественного наследования. Если мне нужно A extends B, но мне также нужна функция, которая принимает C, и я хочу передать A этой функции, тогда я должен сделать интерфейс B или C интерфейсом, а не родительский класс, и сделать его A extends B реализует C или наоборот.
(b) Когда я создаю API для библиотеки или служебной функции, которая будет реализована чем-то вне этой библиотеки или служебной программы, обычно несколькими объектами. Примером этого может служить интерфейс «Comparable» в библиотеке Java.Утилите сортировки нужна функция для сравнения пары объектов. Когда программа вызывает утилиту сортировки с объектами, определенными в вызывающей программе, нельзя ожидать, что утилита сортировки знает, как их сравнивать, поэтому вызывающая программа должна предоставлять функцию сравнения. Подпись для функции сравнения логически входит в интерфейс, который вызывающий объект может реализовать для своих объектов.
(c) Если я хочу опубликовать интерфейс без раскрытия реализации. Это может быть связано с собственными причинами, но на практике это обычно связано с тем, что другой человек или команда реализует интерфейс.
В книге Интерфейсно-ориентированный дизайн есть полезный способ думать об интерфейсах - что они принадлежат клиенту, а не провайдер. Поэтому вместо того, чтобы думать о том, должен ли данный класс быть представлен интерфейсом, подумайте о том, с какими интерфейсами данный класс может захотеть взаимодействовать, а затем найдите или напишите классы, которые соответствуют этим интерфейсам. Интерфейс может быть очень урезанным по сравнению с более длинным списком общедоступных функций в реализующем классе - и, конечно, данный класс может реализовывать несколько интерфейсов. Поиск взаимно однозначного соответствия между классами и интерфейсами, даже если рассматривать интерфейсы как представления полных классов, не так полезен в отношении интерфейсов.
Лучшая практика:
Я «слышал», что в SmallTalk вы всегда начинаете определять интерфейсы до фактической реализации ... Так что я думаю, это действительно зависит от цели и цель проекта, которую вы хотите достичь ...
Делать что-либо до одержимости - неправильно. Это всего лишь инструменты, а мы - мастера.
Превышение интерфейсов - вполне реальная возможность. Игнорирование [незначительного] снижения производительности при вызове каждого метода с использованием виртуальной диспетчеризации также вводит дополнительные параметры в систему. На самом деле это может затруднить отладку и защиту программного обеспечения. Если ваш метод / функция принимает любой старый разработчик интерфейса, вы приняли решение признать, что разработчик мог реализовать интерфейс неправильно или, возможно, даже злонамеренно. То, как ваша программа / библиотека / фреймворк допускает вариации, является важной частью процесса проектирования.
Наличие интерфейсов на объектах и возможность взаимодействия объектов друг с другом только через интерфейсы - это хорошо.
Обратите внимание, что я имею в виду объекты, а не коллекции или другие вещи, которые по сути являются «данными».
Однако наличие реализации класса «IClassName» или аналогичного и наличие этого шаблона во всей кодовой базе отражает плохое понимание концепций интерфейса. Интерфейсы обычно должны объявляться потребляющим классом как объявление «эй, вот услуги, которые мне нужны». Таким образом, интерфейс представляет собой взаимодействие между двумя объектами, а не единое представление объекта в мире. Это также помогает разделить обязанности, поскольку каждый класс имеет дело со своими собственными идеальными интерфейсами и, таким образом, может помочь отделить логику домена от логики реализации.
(править)
Это действительно о разнице между косвенным обращением и абстракцией. Интерфейс, который напрямую соответствует вашему API, - это просто косвенное обращение. Интерфейс, который напрямую соответствует потребностям потребителя, является абстракцией, поскольку он объявляет то, потребитель хочет сделать, и скрывает всю информацию о том, как это делается.
У Зеда Шоу есть хорошая статья по этому поводу.
http://www.zedshaw.com/essays/indirection_is_not_abstraction.html
Я не думаю, что вы можете ошибиться, спрятав все ваши конкретные классы за интерфейсами.
Есть случаи, когда это излишне, но это не стоит вам многого.
Можно даже привести доводы в пользу того, чтобы заставить Domain Objects реализовывать интерфейсы. Преимущества:
Любой класс, который предоставляет «услугу» другому соавтору, должен иметь интерфейс - возможно, вы захотите рубить и изменять реализации для достижения желаемого поведения. Например, вы можете легко заменить службу твиттера электронной почтой.
Любой класс, представляющий объект «значение», не должен нуждаться в интерфейсе, поскольку он служит единственной цели. При тестировании вам даже не потребуется имитировать эти объекты, просто используйте их напрямую, поскольку они определяют конечные узлы вашего графа объектов.
Это в значительной степени субъективный вопрос. Лучшим ответом, вероятно, будет сделать все возможное, чтобы обеспечить как можно больше модульного тестирования вашего кода, но не позволяйте созданию интерфейсов мешать выполнению задач. Если ваш код быстро захламляется, сократите его.
Я видел код, в котором было 400 классов и 400+ интерфейсов для этих классов. Одного копания в этой куче кода с его плохими соглашениями об именах было достаточно, чтобы я вздрогнул. С другой стороны, я имею дело со сторонней проприетарной библиотекой без интерфейсов. Издевательство над этими объектами превращается в мучение.