Нужно ли использовать интерфейс, когда только один класс будет его реализовывать?

243

Разве весь смысл интерфейса в том, что несколько классов придерживаются набора правил и реализаций?

Ламин Саннех
источник
16
Или чтобы облегчить юнит-тест.
11
Разрешение нескольким классам реализовывать интерфейсы и наличие вашего кода зависят от интерфейсов, это важно для изоляции при модульном тестировании. Если вы проводите модульное тестирование, у вас будет другой класс, реализующий этот интерфейс.
StuperUser
2
Reddit обсуждение этого вопроса.
Яннис
6
Открытые поля и методы сами по себе являются «интерфейсом». Если отсутствие полиморфизма намеренно спланировано, то нет смысла использовать интерфейс. Упомянутое юнит-тестирование - это запланированное использование полиморфизма.
mike30

Ответы:

205

Строго говоря, нет, нет, ЯГНИ применяется. Тем не менее, время, которое вы потратите на создание интерфейса, минимально, особенно если у вас есть удобный инструмент для генерации кода, выполняющий большую часть работы за вас. Если вы не уверены, понадобится ли вам интерфейс или нет, я бы сказал, что лучше ошибиться в сторону поддержки определения интерфейса.

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

Яннис
источник
84
+1 тестирование означает, что у вас почти всегда есть две реализации
jk.
24
@YannisRizos Не согласен с вашей последней точкой зрения из-за Яджни. Свернуть интерфейс из открытых методов класса тривиально, как и замена CFoo на IFoo в потребляющих классах. Там нет смысла писать это заранее по необходимости.
Дэн Нили
5
Я до сих пор не уверен, что следую вашим рассуждениям. Так как инструменты генерации кода делают его добавление после факта еще более дешевым, я вижу еще меньше причин для создания интерфейса, прежде чем у вас будет явная потребность в нем.
Дэн Нили
6
Я думаю, что отсутствующий интерфейс - не хороший пример для YAGNI, но для «разбитого окна» и отсутствующей документации. Пользователи класса практически вынуждены кодировать против реализации, а не абстракции, как они должны.
Фабио Фракасси
10
Зачем вам когда-либо загрязнять свою кодовую базу бессмысленными затратами, чтобы удовлетворить какой-то каркас тестирования? Я имею в виду, серьезно, я просто самоучка на стороне клиента JavaScript, пытающаяся разобраться с WTF, что не так с реализациями OOD для C # и Java-разработчиков, с которыми я сталкиваюсь, стремясь стать более универсальным универсалистом, но почему бы и нет? они бьют IDE из ваших рук и не дают вам вернуть ее обратно, пока вы не научитесь писать чистый, разборчивый код, когда поймают, что вы делаете подобные вещи в колледже? Это просто непристойно.
Эрик Реппен
144

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

Вот одно очень полезное правило:

  • Если вы заставляете класс Fooобращаться непосредственно к классу BarImpl, вы решительно обязываетесь менять Fooкаждый раз, когда вы меняете BarImpl. Вы в основном рассматриваете их как одну единицу кода, которая разбита на два класса.
  • Если вы Fooобращаетесь к интерфейсу Bar , вы берете на себя обязательство, чтобы избежать изменений Fooпри изменении BarImpl.

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

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

sacundim
источник
14
Я бы сказал это гораздо чаще, чем один раз, если бы мог. ИМО лучший ответ на этот вопрос.
Фабио Фракасси
4
Если класс выполняет только одну роль (т. Е. Для него определен только один интерфейс), то почему бы не сделать это с помощью открытых / закрытых методов?
Мэтью Финлэй
4
1. Код организации; Наличие интерфейса в собственном файле, содержащем только подписи и комментарии к документации, помогает поддерживать чистоту кода. 2. Фреймворки, которые вынуждают вас предоставлять методы, которые вы бы предпочли оставить закрытыми (например, контейнеры, которые вводят зависимости через общедоступные сеттеры). 3. Отдельная компиляция, как уже упоминалось; если класс Fooзависит от интерфейса , Barто BarImplмогут быть изменены без перекомпиляции Foo. 4. Более детальное управление доступом, чем в публичных / частных предложениях (один и тот же класс предоставляется двум клиентам через разные интерфейсы).
Сакундим
3
И последнее: 5. Клиентов (в идеале) не должно волновать, сколько классов есть в моем модуле или сколько, или даже могут сказать. Все, что они должны видеть, это набор типов , и некоторые фабрики или фасады, чтобы заставить шар катиться. Т.е. я часто вижу значение в инкапсуляции даже того, из каких классов состоит ваша библиотека.
Сакундим
4
@sacundim If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImplПри изменении BarImpl, каких изменений можно избежать в Foo, используя interface Bar? Поскольку сигнатуры и функциональные возможности метода не изменяются в BarImpl, Foo не потребует изменений даже без интерфейса (все назначение интерфейса). Я говорю о сценарии, где только один класс, т.е. BarImplреализует Bar. Для мультиклассового сценария я понимаю принцип инверсии зависимости и то, как его интерфейс полезен.
Шишир Гупта
101

Интерфейсы предназначены для определения поведения, то есть набора прототипов функций / методов. Типы, реализующие интерфейс, будут реализовывать это поведение, поэтому, когда вы имеете дело с таким типом, вы знаете (частично), какое поведение он имеет.

Нет необходимости определять интерфейс, если вы знаете, что определенное им поведение будет использоваться только один раз. ПОЦЕЛУЙ (будь проще, глупый)

superM
источник
Не всегда вы можете использовать интерфейс в одном классе, если этот класс является универсальным классом.
Джон Исайя Кармона
Кроме того, вы можете легко представить интерфейс, когда он, наконец, необходим, в современной IDE с рефакторингом «extract interface».
Ханс-Петер Стёрр
Рассмотрим вспомогательный класс, в котором есть только открытые статические методы и закрытый конструктор - что вполне приемлемо и поддерживается такими авторами, как Joshua Bloch et al.
Даррелл Тиг
63

Хотя в теории у вас не должно быть интерфейса только для того, чтобы иметь интерфейс, ответ Янниса Ризоса намекает на дальнейшие осложнения:

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

Но подождите, это еще не все. Есть больше сценариев, где есть неявные реализации интерфейса. Например, при использовании коммуникационного стека WCF в .NET создается прокси для удаленной службы, которая снова реализует интерфейс.

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

Авнер Шахар-Каштан
источник
2
+1: я не уверен, что здесь применяется YAGNI, поскольку наличие интерфейса и использование фреймворков (например, JMock и т. Д.), Которые используют интерфейсы, могут реально сэкономить ваше время.
Деку
4
@Deco: хорошие фреймворки-насмешки (среди них JMock) даже не нуждаются в интерфейсах.
Майкл Боргвардт
12
Создание интерфейса исключительно из-за ограниченности вашей среды для насмешек кажется мне ужасной причиной. Например, использование EasyMock для насмешки над классом так же просто, как и для интерфейса.
Альб
8
Я бы сказал, что все наоборот. Использование фиктивных объектов в тестировании означает создание альтернативных реализаций для ваших интерфейсов по определению. Это верно, независимо от того, создаете ли вы свои собственные классы FakeImplementation или позволяете фальшивым фреймворкам делать тяжелую работу за вас. Могут быть некоторые фреймворки, такие как EasyMock, которые используют различные хаки и низкоуровневые трюки, чтобы высмеивать конкретные классы - и больше мощности для них! - но концептуально фиктивные объекты являются альтернативными реализациями контракта.
Авнер Шахар-Каштан
1
Вы не запускаете тесты в производстве. Почему макет для класса, которому не нужен интерфейс, нужен интерфейс?
Эрик Реппен
33

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

Существует реальная стоимость создания Foo / FooImpl для всего. IDE может создать интерфейс / реализацию бесплатно, но когда вы перемещаетесь по коду, вы получаете дополнительную когнитивную нагрузку от F3 / F12 при переходе foo.doSomething()к сигнатуре интерфейса, а не к фактической реализации, которую вы хотите. Плюс у вас есть беспорядок двух файлов вместо одного для всего.

Так что вы должны делать это только тогда, когда вам это действительно нужно для чего-то.

Теперь обращаемся к контраргументам:

Мне нужны интерфейсы для структур внедрения Dependency

Интерфейсы для поддержки платформ являются устаревшими. В Java интерфейсы раньше были требованием для динамических прокси, pre-CGLIB. Сегодня вам обычно это не нужно. Это считается прогрессом и благом для разработчиков, так как они вам больше не нужны в EJB3, Spring и т. Д.

Мне нужны макеты для юнит-тестирования

Если вы пишете свои собственные макеты и имеете две фактические реализации, тогда интерфейс уместен. Скорее всего, мы бы не обсуждали это в первую очередь, если бы в вашей кодовой базе были и FooImpl, и TestFoo.

Но если вы используете фальшивый фреймворк, такой как Moq, EasyMock или Mockito, вы можете имитировать классы и вам не нужны интерфейсы. Это аналогично настройке foo.method = mockImplementationна динамическом языке, где можно назначать методы.

Нам нужны интерфейсы, чтобы следовать принципу инверсии зависимости (DIP)

DIP говорит, что вы строите, чтобы зависеть от контрактов (интерфейсов), а не от реализаций. Но класс - это уже контракт и абстракция. Вот для чего нужны публичные / частные ключевые слова. В университете каноническим примером было что-то вроде класса Matrix или Polynomial - потребители имеют общедоступный API для создания матриц, добавления их и т. Д., Но им не нужно заботиться о том, реализована ли матрица в разреженной или плотной форме. Не было IMatrix или MatrixImpl, необходимых для доказательства этой точки зрения.

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

Это является следствием высказывания дяди Боба Мартина о насмешках - вам нужно только высмеивать основные архитектурные границы. В веб-приложении HTTP и доступ к БД являются основными границами. Все вызовы класса / метода между ними не являются. То же самое касается DIP.

Смотрите также:

wrschneider
источник
4
Классы мошенничества, а не интерфейсы (по крайней мере, в Java и C #) следует считать устаревшими, поскольку нет способа предотвратить запуск конструктора суперкласса, что может привести к непреднамеренному взаимодействию вашего ложного объекта со средой. Мокерство интерфейса безопаснее и проще, потому что вам не нужно думать о коде конструктора.
Жюль
4
У меня не было проблем с классами насмешек, но я был разочарован тем, что навигация IDE не работала должным образом. Решение реальной проблемы превосходит гипотетическую.
wrschneider
1
@Jules Вы можете смоделировать конкретный класс, включая его конструктор, в Java.
assylias
1
@assylias как помешать запуску конструктора?
Жюль
2
@Jules Это зависит от вашей среды моделирования - например, с помощью jmockit вы можете просто написать, new Mockup<YourClass>() {}и весь класс, включая его конструкторы, будет смоделирован, будь то интерфейс, абстрактный класс или конкретный класс. Вы также можете «переопределить» поведение конструктора, если хотите. Я полагаю, есть эквивалентные способы в Mockito или Powermock.
assylias
22

Похоже, что ответы с обеих сторон забора можно суммировать следующим образом:

Хорошо спроектируйте и разместите интерфейсы там, где нужны интерфейсы.

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

Для (ужасно надуманного) примера, скажем, вы создаете Carкласс. В вашем классе вам наверняка понадобится слой пользовательского интерфейса. В этом конкретном примере, он принимает форму IginitionSwitch, SteeringWheel, GearShift, GasPedal, и BrakePedal. Так как эта машина содержит AutomaticTransmission, вам не нужно ClutchPedal. (А поскольку это ужасная машина, здесь нет кондиционера, радио или сиденья. На самом деле половицы тоже отсутствуют - вам просто нужно держаться за руль и надеяться на лучшее!)

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

Вы могли бы иметь интерфейс, который был бы похож на это:

Interface ICabin
    Event IgnitionSwitchTurnedOn()
    Event IgnitionSwitchTurnedOff()
    Event BrakePedalPositionChanged(int percent)
    Event GasPedalPositionChanged(int percent)
    Event GearShiftGearChanged(int gearNum)
    Event SteeringWheelTurned(float degree)
End Interface

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

Это та же самая причина, по которой вы не тестируете модульные частные члены - они существуют только для поддержки общедоступного API, и, следовательно, их поведение должно быть проверено путем тестирования API.

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


РЕДАКТИРОВАТЬ:

Часто (в том числе в этом ответе) вы будете читать такие вещи, как «домен», «зависимость» (часто в сочетании с «инъекцией»), которые ничего не значат для вас, когда вы начинаете программировать (они точно не значит что-нибудь для меня). Для домена это означает именно то, на что это похоже:

Территория, на которой осуществляется господство или власть; владения суверена или содружества или тому подобное. Также используется в переносном смысле. [WordNet sense 2] [1913 Вебстер]

В терминах моего примера - давайте рассмотрим IgnitionSwitch. В легковом автомобиле переключатель зажигания отвечает за:

  1. Аутентификация (не идентификация) пользователя (ему нужен правильный ключ)
  2. Подача тока на стартер, чтобы он действительно мог завести автомобиль
  3. Подает ток в систему зажигания, чтобы она могла продолжать работать
  4. Отключение тока, чтобы машина остановилась.
  5. В зависимости от того, как вы смотрите на это, в большинстве (всех?) Новых автомобилей есть переключатель, который предотвращает удаление ключа из замка зажигания, когда коробка передач находится вне парка, так что это может быть частью его домена. (На самом деле это означает, что мне нужно переосмыслить и перепроектировать мою систему ...)

Эти свойства составляют область того IgnitionSwitch, о чем он знает и за что отвечает.

IgnitionSwitchНе несет ответственности за GasPedal. Переключатель зажигания полностью игнорирует педаль газа во всех отношениях. Они оба работают совершенно независимо друг от друга (хотя без них оба были бы бесполезны!).

Как я уже говорил, это зависит от вашего дизайна. Вы могли бы создать, IgnitionSwitchкоторый имел два значения: On ( True) и Off ( False). Или вы можете создать его для аутентификации предоставленного ключа и множества других действий. Трудно быть разработчиком, чтобы решить, где рисовать линии на песке - и, честно говоря, большую часть времени это абсолютно относительно. Эти строки в песке важны, хотя - это то, где находится ваш API, и, следовательно, где должны быть ваши интерфейсы.

Уэйн Вернер
источник
Не могли бы вы рассказать подробнее о том, что вы подразумеваете под «иметь свой собственный домен»?
Ламин Саннех
1
@LaminSanneh, разработано. Это помогает?
Уэйн Вернер
8

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

Стейн
источник
8

Из MSDN :

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

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

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

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

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

LWM
источник
5

Чтобы ответить на вопрос: в этом есть нечто большее.

Одним из важных аспектов интерфейса является намерение.

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

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

Джошуа Дрейк
источник
5

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

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

Как только контракт определен, две или более команды могут работать независимо. Скажем, вы работаете над модулем A, и это зависит от модуля B, сам факт создания интерфейса на B позволяет продолжить вашу работу, не беспокоясь о реализации B, потому что все детали скрыты интерфейсом. Таким образом, распределенное программирование становится возможным.

Хотя модуль B имеет только одну реализацию своего интерфейса, этот интерфейс все еще необходим.

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

Хуэй Ван
источник
2
Каждый класс может иметь открытый интерфейс (открытые методы) и закрытый интерфейс (подробности реализации). Я мог бы утверждать, что публичный интерфейс класса - это контракт. Вам не нужен дополнительный элемент для работы с этим контрактом.
Fuhrmanator
4

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

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

interface NameChangeListener { // Implemented by a lot of people
    void nameChanged(String name); 
} 

interface NameChangeCount { // Only implemented by my class
    int getCount();
}

class NameChangeCounter implements NameChangeListener, NameChangeCount {
    ...
}

class SomeUserInterface {
    private NameChangeCount currentCount; // Will never know that you can change the counter
}

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

interface SomeRepository { // Guarantee that the external library details won't leak trough
    ...
}

class OracleSomeRepository implements SomeRepository { 
    ... // Oracle prefix allow us to quickly know what is going on in this class
}

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

package project.domain;

interface UserRequestSource {
    public UserRequest getLastRequest();
}

class UserBehaviorAnalyser {
    private UserRequestSource requestSource;
}

package project.ui;

class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
    // UI concern, no need for my domain object to know about this method.
    public void displayLabelInErrorMode(); 

    // They most certainly need to know about *that* though
    public UserRequest getLastRequest();
}

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

interface Sender {
    void sendMessage(Message message)
}

class PacketSender implements Sender {
    void sendMessage(Message message);
    void setPacketSize(int sizeInByte);
}

class Throttler { // This class need to have full access to the object
    private PacketSender sender;

    public useLowNetworkUsageMode() {
        sender.setPacketSize(LOW_PACKET_SIZE);
        sender.sendMessage(new NotifyLowNetworkUsageMessage());

        ... // Other details
    }
}

class MailOrder { // Not this one though
    private Sender sender;
}

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

Лоран Бурго-Рой
источник
2

Интерфейсы действительно важны, но постарайтесь контролировать их количество.

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

http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-application

Это его первый пост из всей серии, так что продолжайте читать!

Лорд Спа
источник
Код «нарезанных спагетти» также известен как код равиоли c2.com/cgi/wiki?RavioliCode
CurtainDog
Для меня это больше похоже на код лазаньи или код пахлавы - код слишком много слоев. ;-)
dodgy_coder
2

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

dodgy_coder
источник
2

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

Джон Деметриу
источник
1

Не всегда нужно определять интерфейс для класса.

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

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

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

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

Флориан Ф
источник