Это неправильно, чтобы каждый конкретный класс наследовал от интерфейса? - PullRequest
10 голосов
/ 18 февраля 2010

Это ответ на некоторые комментарии , сделанные Зедом Шоу в своем блоге давным-давно.

Затем эксперты уйдут, чтобы внедрить свою Пылающую Вавилонскую башню.без каких-либо комментариев, ужасно сложные тесты с включенной имитацией, убедившись, что КАЖДЫЙ ОДИН КЛАСС имеет ИНТЕРФЕЙС, и заканчивая каждый класс «Impl», потому что, ну, в общем, это лучшая практика.Хитрость в равной степени, и я заметил, что эти фреймворки используют постфикс Impl , но экономно.В моем коде я использую интерфейсы везде, потому что мне сказали, что это облегчает насмешку и т. Д. У меня есть наивное понимание проблемы?(Возможно, фиктивные фреймворки могут работать с абстрактными классами или классами, я не знаю. Я никогда не пробовал) Для своих конкретных реализаций я выбрал соглашение Spring, заключающееся в добавлении префикса имени реализации к слову Default.

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

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

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

Ответы [ 13 ]

17 голосов
/ 18 февраля 2010

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

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

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

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

6 голосов
/ 18 февраля 2010

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

6 голосов
/ 18 февраля 2010

Лучшей практикой является:

  • Выберите соглашение и будьте последовательны в этом; и
  • Не переусердствуйте, заставляя все реализовывать интерфейс.
2 голосов
/ 18 февраля 2010

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

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

2 голосов
/ 18 февраля 2010

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

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

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

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

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

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

2 голосов
/ 18 февраля 2010

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

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

1 голос
/ 18 февраля 2010

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

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

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

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 голос
/ 18 февраля 2010

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

Бывают случаи, когда это излишне, но это не будет стоить вам дорого.

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

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

Хорошо иметь интерфейсы на объектах и ​​иметь объекты, общающиеся друг с другом только через интерфейсы.

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

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

(edit)

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

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

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

1 голос
/ 18 февраля 2010

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

...