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

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

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

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

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

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

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

Ответ 1

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

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

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

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

Ответ 2

Согласно wikipedia,

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

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

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

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

  • IWidgetManager
  • IWidgetManager2
  • IWidgetManager3

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

По-моему, это суть хорошего дизайна кода.

Ответ 5

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

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

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

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

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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

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

Ответ 9

Я думаю, что вы описываете метод down-down.
Вам не обязательно это делать, но иногда он действительно помогает.

Ответ 10

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

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

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

Ответ 11

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

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

Это смешно. Этот тип мышления, вероятно, является тем, что порождает такие комментарии, как те, которые сосредоточены вокруг эпизода № 38 StackOverflow.

Ответ 12

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

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

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

Ответ 13

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

Ответ 14

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

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

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

Ответ 15

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