Когда нужны интерфейсы?

(В контексте.NET для чего стоит)

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

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

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

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

Я думаю о программном приложении как о графике. Полный график - наихудший случай, имеющий n(n-1)/2. Это означает, что каждый класс говорит с каждым классом. Запутанная паутина. N-1 является лучшим, в котором их строгая иерархия общения. Добавление другого интерфейса просто для компенсации нового необходимого члена добавляет вершины к графу, что означает больше ребер и более сильную реализацию уравнения n(n-1)/2. Композиция без интерфейсов больше похожа на миксин. Только выбранные классы используют определенные методы. Благодаря интерфейсу все классы вынуждены использовать члены, даже если они им не нужны. Композиция / миксин подход не добавляет новые ненужные края.

15 ответов

Решение

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

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

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

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

Согласно википедии,

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

Вот что делает интерфейсы такими полезными.

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

Я просто догадываюсь здесь, но звучит так, будто этот парень происходит из старой школы COM (я могу ошибаться, конечно!). Заставляет меня задуматься над всем кодом, над которым я работал, где я видел такие вещи:

  • IWidgetManager
  • IWidgetManager2
  • IWidgetManager3

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

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

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

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

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

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

Если вы хотите понять, когда следует использовать интерфейсы и как они используются, я думаю, вам следует взглянуть на книгу " Head First Desing Patterns".

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

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

Интерфейсы помогут вам сохранить зависимости от абстракций.

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

На мой взгляд, это суть хорошего дизайна кода.

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

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

Для лучшего объяснения попробуйте взглянуть на литературу по инверсии управления.

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

Разве это не композиция? Почему бы не использовать композицию без интерфейсов? Более гибкий."

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

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

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

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

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

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

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

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

  • IComparable (ВЫ сами решаете, как ваши объекты сравниваются друг с другом)
  • IQueryable (LINQ кто-нибудь?)
  • IDisposable (Держите ваши using Скажите удобно)

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

Я хотел бы обратиться к руководству по разработке.net по этому адресу: http://msdn.microsoft.com/en-us/library/ms229013.aspx

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

Мое эмпирическое правило состоит в том, чтобы реализовать каждый интерфейс BCL, который имеет смысл, и добавлять мои собственные интерфейсы в мои проекты только тогда, когда он действительно обеспечивает что-то очень ценное. Вместо IWidgetManager, IWidgetManager2 и т. Д... я бы предпочел иметь абстрактный класс WidgetManager и реализовывать конкретные методы по мере необходимости.

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

В прошлом некоторые утверждали, что у каждого класса в системе должен быть интерфейс, но это широко признано излишним. В настоящее время используются интерфейсы, в которых экземпляры классов могут изменяться как часть системной операции (для представления состояния): шаблоны GoF, такие как "Стратегия" и "Команда", фиксируют это использование, и / или части системы необходимо заменить для тестирования (т.е. зависимость инъекции). Если разработчики следуют методикам разработки на основе тестирования, ключевые объекты инфраструктуры будут иметь интерфейсы. Это позволяет тестам "макетировать" эти объекты, чтобы проверить поток управления системой. Существует связь между этим использованием интерфейса и принципом подстановки Лискова (один из принципов проектирования ОО)

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

Этот человек звучит так, как будто он неправильно понял концепцию программирования интерфейса. Это не относится к программированию с использованием только interface ключевое слово в.Net/Java или в классе, где нет ничего, кроме чисто виртуальных функций в C++. Интерфейс, на который ссылаются в этом принципе, - это высокоуровневая конструкция, которая инкапсулирует низкоуровневые компоненты системы. Как и в случае умножения, он заключает в себе идею добавления числа к себе определенного количества повторений. Но когда мы говорим 3 * 2, нам все равно, если 3 + 3, 2 + 2 + 2 или (2 + 2) + 2, где часть в скобках кэшируется. Пока мы получаем 6 от этого.

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

Это забавно. Видение этого парня, вероятно, является тем, что породило комментарии, такие как те, которые сосредоточены вокруг эпизода Stackru #38.

Одной из наиболее важных причин использования интерфейса является тот факт, что они позволяют нам писать модульные тесты для наших классов и передавать наши собственные зависимости. Помещая зависимость за интерфейсом, мы открываем "Швы" в коде нашего приложения, где легко могут быть написаны модульные тесты. Я не вижу этого угла тестирования, упомянутого во многих ответах, но очень важно понимать, что без интерфейсов эти зависимости (такие как веб-служба или ссылка на файловую систему) могут стать очень трудными для тестирования или, в лучшем случае, довольно условны. Я написал здесь пост: http://jeffronay.com/why-use-an-interface/, в котором более подробно рассматриваются примеры кода, показывающие класс Service без интерфейса, а затем тот же класс, переписанный с использованием интерфейс для демонстрации гибкости тестирования при использовании интерфейсов.

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

Похоже, ваш друг серьезно использовал интерфейсы.

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

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

Другие вопросы по тегам