Почему интерфейсы полезны?

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

C # - отличный язык, но иногда это создает ощущение, что первая Microsoft создает проблему (не разрешая множественное наследование), а затем предоставляет решение, которое довольно утомительно.

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

150 голосов | спросил 5 revs, 4 users 44%
Pankaj Upadhyay
1 Jam1000000amThu, 01 Jan 1970 03:00:00 +030070 1970, 03:00:00

19 ответов


147
  

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

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

Тем не менее, интерфейсы являются довольно слабым способом представления контрактных обязательств. Если вам нужен более сильный и гибкий способ представления контрактных обязательств, посмотрите на функцию «Кодовые контракты», которая поставляется с последней версией Visual Studio.

  

C # - отличный язык, но иногда возникает ощущение, что первая Microsoft создает проблему (не позволяя множественное наследование), а затем предоставляет решение, которое довольно утомительно.

Хорошо, я рад, что тебе это нравится.

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
223

введите описание изображения здесь>> </p>

<p> Итак, в этом примере PowerSocket ничего не знает о других объектах. Все объекты зависят от Power, обеспечиваемого PowerSocket, поэтому они реализуют IPowerPlug, и при этом они могут подключаться к нему. </p>

<p> Интерфейсы полезны, поскольку они предоставляют контракты, которые объекты могут использовать для совместной работы, без необходимости знать что-либо еще друг о друге. </p></body></html>

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
144
  

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

Точка интерфейсов не поможет вам запомнить, какой метод реализовать, здесь определить контракт . В представьте пример P.Brian.Mackey (который оказывается неправильным , но нам все равно), IEnumerable определяет контракт между foreach и любой перечислимой вещью. В нем говорится: «Кто бы вы ни были, до тех пор, пока вы придерживаетесь контракта (реализуете IEnumerable), я обещаю вам, что буду перебирать все ваши элементы». И это здорово (для не динамического языка).

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
37

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

При написании тестов вы обнаружите, что конкретные классы не будут работать в тестовой среде.

Пример. Вы хотите протестировать класс, который зависит от класса Data Access Service . Если этот класс говорит с веб-службой или с базой данных - ваш тестовый тест не будет работать в тестовой среде (плюс он превратился в тест интеграции).

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

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
29

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

  

Как часто вы используете их и что заставляет вас делать это?

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

Нужны разные алгоритмы для выполнения операций с одними и теми же данными? Используйте интерфейс ( см. Шаблон стратегии )!

Вы хотите использовать разные реализации списков? Код для интерфейса и вызывающего абонента не нужно беспокоиться о реализации!

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
12

Вы, вероятно, использовали foreach и обнаружили, что это очень полезный инструмент итерации. Знаете ли вы, что для этого требуется интерфейс, IEnumerable ?

Это, безусловно, конкретный случай, говорящий о полезности интерфейса.

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
11

Интерфейсы предназначены для кодирования объектов, таких как штекер, для подключения к домашней электропроводке. Не могли бы вы припаять ваше радио прямо к вашей домашней электропроводке? Как насчет вашего пылесоса? Конечно нет. Штепсельная вилка и розетка, в которую она входит, образуют «интерфейс» между проводкой вашего дома и устройством, которое нуждается в мощности от него. Проводка вашего дома не должна ничего знать о устройстве, кроме него, используется трехштырьковая заземленная вилка и требует электрической мощности при 120 В переменного тока = 15 А. И наоборот, устройство не требует тайного знания о том, как ваш дом подключен, кроме того, что он имеет один или несколько трехштырьковых выходов, удобно расположенных, которые обеспечивают 120VAC <= 15A.

Интерфейсы выполняют очень похожую функцию в коде. Объект может объявить, что конкретная переменная, параметр или тип возврата имеет тип интерфейса. Интерфейс не может быть создан непосредственно с помощью ключевого слова new, но мой объект может быть предоставлен или найти реализацию этого интерфейса, с которым ему нужно будет работать. Как только объект имеет свою зависимость, ему не нужно точно знать, что такое зависимость, он просто должен знать, что он может вызывать методы X, Y и Z в зависимости. Реализации интерфейса не должны знать, как они будут использоваться, они просто должны знать, что они должны будут предоставлять методы X, Y и Z с помощью специальных подписей.

Таким образом, абстрагируя несколько объектов за одним и тем же интерфейсом, вы предоставляете общий набор функций любому потребителю объектов этого интерфейса. Вам не обязательно знать, что объект - это, например, Список, Словарь, LinkedList, OrderedList или что-то еще. Поскольку вы знаете, что все это IEnumerables, вы можете использовать методы IEnumerable для прохождения каждого элемента в этих коллекциях по одному. Вам не обязательно знать, что выходной класс - это ConsoleWriter, FileWriter, NetworkStreamWriter или даже MulticastWriter, который использует другие типы писателей; все, что вам нужно знать, это то, что они все IWriters (или что-то еще), и поэтому у них есть метод «Write», в который вы можете передать строку, и эта строка будет выводиться.

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
7

Несмотря на то, что для программиста (по крайней мере, по крайней мере), для того чтобы иметь множественное наследование, очевидно, что это почти тривиальное упущение, и вы должны (в большинстве случаев) не полагаться на множественное наследование. Причины этого сложны, но если вы действительно хотите узнать об этом, рассмотрите опыт двух самых известных ( индекс TIOBE ) языки программирования, которые его поддерживают: C ++ и Python (3-й и 8-й порядочно).

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

Для C ++, Google «алмазная иерархия C ++», и вот уродство, которое вот-вот должно вас охватить. Профили C ++ знают, как использовать множественное наследование. Все остальные обычно просто играют, не зная, какими будут результаты. Еще одна вещь, которая показывает, насколько полезны интерфейсы, заключается в том, что во многих случаях классу может потребоваться полностью переопределить поведение своего родителя. В таких случаях родительская реализация не нужна и только нагружает дочерний класс памятью для частных переменных родителя, что может не иметь значения в возрасте C #, но имеет значение, когда вы выполняете встроенное программирование. Если вы используете интерфейс, эта проблема отсутствует.

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

Кроме того, исторически идея интерфейса уходит корнями далеко раньше, чем спецификации Microsoft C #. Большинство людей считают, что C # является обновлением по Java (в большинстве случаев), и угадать, откуда C # получил свои интерфейсы от - Java. Протокол является более старым словом для той же концепции, и он намного старше .NET.

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
6

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

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
5

Я лично люблю абстрактный класс и использую его больше, чем интерфейс. Основное различие заключается в интеграции с интерфейсами .NET, такими как IDisposable, IEnumerable и т. Д. ... и с COM-взаимодействием. Кроме того, интерфейс немного меньше усилий для написания, чем абстрактный класс, и класс может реализовать более одного интерфейса, в то время как он может наследовать только один класс.

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

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

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
4

Есть две основные причины:

  1. Отсутствие множественного наследования. Вы можете наследовать один базовый класс и реализовать любое количество интерфейсов. Это единственный способ «сделать» множественное наследование в .NET.
  2. Совместимость COM. Все, что нужно будет использовать «старым» технологиям, должно иметь определенные интерфейсы.
ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
4

Я могу сказать, что я отношусь к этому. Когда я впервые начал изучать OO и C #, я тоже не получил интерфейсы. Ничего страшного. Нам просто нужно прийти к чему-то, что позволит вам оценить удобства интерфейсов.

Позвольте мне попробовать два подхода. И простите меня за обобщения.

Попробуйте 1

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

Вы спрашиваете: «Эй, вы родились в Соединенных Штатах?» Это наследование.

Или вы спрашиваете: «Эй, ты говоришь по-английски»? Это интерфейс.

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

Это нормально полагаться на наследование. Если вам нужен кто-то, кто говорит по-английски, любит чай и любит футбол, вам лучше подавать просьбу о Британе. :)

Попробуйте 2

Хорошо, попробуем еще один пример.

Вы используете разные базы данных, и вам нужно реализовать абстрактные классы для работы с ними. Вы передадите свой класс в какой-либо класс от поставщика БД.

public abstract class SuperDatabaseHelper
{
   void Connect (string User, string Password)
}

public abstract class HiperDatabaseHelper
{
   void Connect (string Password, string User)
}

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

interface ISuperDatabaseHelper
{
  void Connect (string User, string Password)
}

interface IHiperDatabaseHelper
{
   void Connect (string Password, string User)
}

Теперь есть что-то, с чем мы можем работать - по крайней мере, на C #, где мы можем явно реализовать интерфейсы.

public class MyDatabaseHelper : ISuperDatabaseHelper, IHiperDatabaseHelper
{
   IHiperDataBaseHelper.Connect(string Password, string User)
   {
      //
   }

   ISuperDataBaseHelper.Connect(string User, string Password)
   {
      //
   }

}

Заключение

Примеры не самые лучшие, но я думаю, что это становится точным.

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
3

Использование интерфейсов помогает системе оставаться развязанной и, таким образом, легче реорганизовать, изменить и повторно развернуть. Это очень основное понятие объектно-ориентированной ортодоксии, и я впервые узнал об этом, когда гуру C ++ сделали «чистые абстрактные классы», которые вполне эквивалентны интерфейсам.

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
3

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
2

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

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
2

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
1

Реальная реализация:

Вы можете указать объект как тип интерфейса:

IHelper h = (IHelper)o;
h.HelperMethod();

Вы можете создать список интерфейса

List<IHelper> HelperList = new List<IHelper>();

С помощью этих объектов вы можете получить доступ к любым из методов или свойств интерфейса. Таким образом вы можете определить интерфейс для своей части программы. И постройте логику вокруг него. Затем кто-то другой может реализовать ваш интерфейс в своих бизнес-объектах. Если изменение BO они могут изменить логику для компонентов интерфейса и не требуют изменения логики для вашей части.

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
0

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

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

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

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

Например, вы можете заменить ...

Me.PublishDate.Clear()
Me.Subject.Clear()
Me.Body.Clear()

... с:

For Each ctl As IClear In Me.Controls.OfType(Of IClear)()
    ctl.Clear()
Next

Что эффективно звучит так:

  

Слушайте, слушайте! Будут ли все, кто понимает расчистку, пожалуйста, Clear сейчас!

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57
0

Ниже приведен псевдокод:

class MyClass{

    private MyInterface = new MyInterfaceImplementationB();

    // Code using Thingy 

}

interface MyInterface{

    myMethod();

}

class MyInterfaceImplementationA{ myMethod(){ // method implementation A } }

class MyInterfaceImplementationB{ myMethod(){ // method implementation B } }

class MyInterfaceImplementationC{ myMethod(){ // method implementation C } }

Последние классы могут быть совершенно разными реализациями.

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

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

ответил Eric Lippert 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2011 19:09:57 +0400 2011, 19:09:57

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132