Это плохая привычка не использовать интерфейсы? [закрыто]


40

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

Также я создаю подклассы и суперклассы (создавая свои собственные классы) редко в своем коде.

  • Это плохо?
  • Вы бы предложили изменить этот стиль?
  • У этого стиля есть побочные эффекты?
  • Это потому, что я не работал над крупными проектами?

10
Какой это язык?

2
Кроме какого языка, что это за парадигма?
Армандо

1
Разве понятие «интерфейс» не выходит за рамки языка? Java имеет встроенный тип Interface, но разработчики на C ++ также часто создают интерфейсы (с префиксом I), и все они служат одной цели.
Рикет

4
@Ricket: нет, не совсем. Проблема в том, что C ++ предлагает множественное наследование классов. В C ++ «интерфейс» гораздо более концептуален, и в действительности мы в основном используем то, что они определяют как абстрактный базовый класс. #
DeadMG

2
@Ricket нет, особенно если вы работаете на функциональном языке, а не на процедурном или ООП.
альтернатива

Ответы:


36

Есть несколько причин, почему вы можете захотеть использовать интерфейсы:

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

http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx

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


13
Его язык не указан, а ваш язык слишком конкретен - например, в C ++ структура действительно может наследовать от класса, и вы можете многократно наследовать неабстрактные базы.
DeadMG

2
Этот ответ, кажется, C #, но также относится к Java, если вы уберете # 4, и только в некотором роде применяется к C ++, потому что в C ++ допускается множественное наследование.
Рикет

2
Также я не согласен с последним утверждением. Я думаю, что есть много хороших практик, которые программисты должны делать, но не делают, потому что они чувствуют, что они им не нужны. Я думаю, что спрашивающий очень мудр, оглядываясь по сторонам, видя, как все остальные делают это, и думая, может, он тоже должен это делать, но сначала спрашивая «почему».
Рикет

3
@Ricket: почему в четырех пулевых точек моего ответа. Если ни одна из этих причин не применима, вам не нужен интерфейс.
Роберт Харви

17

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

Я бы сказал, что использование интерфейсов чаще всего не является плохой практикой. В настоящее время я читаю «Эффективные C # - 50 конкретных способов улучшить ваш C #» Билла Вагнера. Пункт номер 22 гласит, и я цитирую, «Предпочитаю определение и реализацию интерфейсов наследования».

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

Пару цитат из книги Билла Вагнера ...

все производные классы немедленно включают это поведение. Добавление члена к интерфейсу нарушает все классы, которые реализуют этот интерфейс. Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Вы освобождаете интерфейс как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Вы освобождаете интерфейс как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ".

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

«Использование интерфейсов для определения API для класса обеспечивает большую гибкость».

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

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


2
Ну, я прочитал это и подумал, что это хороший ответ.
Эрик Кинг,

1
@ mc10 Не уверен, в чем проблема. Он сослался на книгу, и нижняя половина его ответа - это просто цитаты из этой книги, которые он ясно дает вам знать, если вы не хотите тратить свое время.
Пит

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

3
ха-ха, если это д-р, я не уверен, что вам понравится вся эта вещь StackExchange-высокое качество-ответы.
Nic

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

8

Единственное, что не было упомянуто, это тестирование: во всех библиотеках C # mocking, а также в некоторых Java, классы не могут быть смоделированы, если они не реализуют интерфейс. Это приводит к тому, что многие проекты, следуя методам Agile / TDD, дают каждому классу собственный интерфейс.

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


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

Например, .Net Framework имеет несколько классов, которые хранят списки вещей , но все они хранят эти вещи по-разному. Таким образом, имеет смысл иметь абстрактныйIList<T> интерфейс, который можно реализовать разными способами.

Вы также должны использовать его, если вы хотите, чтобы 2+ класса были взаимозаменяемыми или заменяемыми в будущем. Если в будущем появится новый способ хранения списков AwesomeList<T>, то при условии, что вы использовали его IList<T>в своем коде, его изменение AwesomeList<T>будет означать лишь изменение нескольких десятков строк, а не нескольких сотен / тысяч.


5

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


4

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

Проблема в том, что в таких языках, как C # и Java со слабыми обобщениями времени компиляции, вы можете в конечном итоге нарушить DRY, потому что нет способа написать один метод, который может иметь дело с более чем одним классом, если все классы не наследуются от одной и той же базы. C # 4 dynamic может справиться с этим, хотя.

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


1
Причина для понижения?
DeadMG

Не уверен, есть голос. Это был хороший ответ.
Брайан Бетчер

3

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


3

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

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


2

Если вы говорите об использовании Java, одной из причин использования интерфейсов является то, что они позволяют использовать прокси-объекты без библиотек генерации кода. Это может быть существенным преимуществом, когда вы работаете со сложной средой, такой как Spring. Более того, для некоторых функций требуются интерфейсы: RMI является классическим примером этого, поскольку вы должны описать предоставляемые вами функциональные возможности в терминах интерфейсов (которые наследуются java.rmi.Remote), как бы вы их ни реализовывали.


1

Вещи меняются ( MS Moles ), но главная причина, по которой я думаю, что кодировать почти исключительно для интерфейсов - это то, что их легко смоделировать и естественным образом вписать в архитектуру IoC.

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


0

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

Плохо:

class B; // forward declaration
class A
{
  B* b;
};

class B
{
  A* a;
}

Неплохо:

class PartOfClassB_AccessedByA
{
};

class A
{
  PartOfClassB_AccessedByA* b;
};

class B : public PartOfClassB_AccessedByA
{
  A* a;
}

Обычно A, B, PartOfClassB_AccessedByA реализованы с отдельными файлами.


0

Программирование на основе интерфейса помогает сделать код более гибким и более простым для тестирования. Классы реализации могут быть изменены без касания клиентского кода [Гибкость]. При тестировании кода можно заменить реальное программирование на основе oInterface, что делает код более гибким и более простым для тестирования. Классы реализации могут быть изменены без касания клиентского кода [Гибкость]. При тестировании кода можно заменить фактический объект фиктивными объектами [Testable] .bject на фиктивные объекты [Testable].

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.