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

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

27 голосов
/ 20 февраля 2009

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

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

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

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

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

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

Ответы [ 15 ]

40 голосов
/ 20 февраля 2009

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

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

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

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

7 голосов
/ 20 февраля 2009

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

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

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

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

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

  • IWidgetManager
  • IWidgetManager2
  • IWidgetManager3

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

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

5 голосов
/ 20 февраля 2009

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

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

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

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

3 голосов
/ 23 февраля 2009

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

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

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

2 голосов
/ 21 февраля 2009

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

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

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

2 голосов
/ 20 февраля 2009

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

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

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

2 голосов
/ 20 февраля 2009

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

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

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

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

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

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

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

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

1 голос
/ 20 февраля 2009

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

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

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

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

0 голосов
/ 05 июня 2009

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

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

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

...