Я нахожусь в процессе обучения, когда чувствую, что должен больше узнать об интерфейсах.
Я часто читаю о них, но мне кажется, что я не могу их понять.
Я читал примеры, такие как: базовый класс Animal, с интерфейсом IAnimal для таких вещей, как «Walk», «Run», «GetLegs» и т. Д., Но я никогда не работал над чем-то и чувствовал, что «эй, я должен использовать интерфейс Вот!"
Чего мне не хватает? Почему мне так сложно понять это? Меня просто пугает тот факт, что я, возможно, никогда не осознаю конкретной потребности в ней - в основном из-за какого-то недостающего аспекта их понимания! Это заставляет меня чувствовать, что я что-то упускаю из-за того, что являюсь разработчиком! Если у кого-то был подобный опыт, и у него был прорыв, я был бы признателен за несколько советов о том, как понять эту концепцию. Спасибо.
источник
Ответы:
это решает эту конкретную проблему:
у вас есть a, b, c, d 4 разных типов. по всему коду у вас есть что-то вроде:
почему бы им не реализовать IProcessable, а затем сделать
это будет значительно лучше, если вы добавите, скажем, 50 типов классов, которые все делают одно и то же.
Еще одна конкретная проблема:
Вы когда-нибудь смотрели на System.Linq.Enumerable? Он определяет тонну методов расширения, которые работают с любым типом, который реализует IEnumerable. Поскольку все, что реализует IEnumerable, в основном говорит: «Я поддерживаю итерацию в неупорядоченном шаблоне типа foreach», вы можете определить сложные варианты поведения (Count, Max, Where, Select и т. Д.) Для любого перечислимого типа.
источник
foreach(IMyCompanyWidgetFrobber a in list) a.Frob(widget, context);
Мне очень нравится ответ Джимми, но я чувствую, что должен что-то добавить к нему. Ключом ко всему является «умение» в IProcess, способное, Он указывает на возможность (или свойство, но означает «внутреннее качество», а не в смысле свойств C #) объекта, реализующего интерфейс. IAnimal, вероятно, не является хорошим примером для интерфейса, но IWalkable может быть хорошим интерфейсом, если ваша система имеет много возможностей для работы. У вас могут быть классы, полученные от животных, такие как собака, корова, рыба, змея. Первые два, вероятно, реализуют IWalkable, последние два не ходят, поэтому они не будут. Теперь вы спрашиваете: «Почему бы просто не иметь другого суперкласса, WalkingAnimal, от которого происходят собаки и коровы?». Ответ - когда у вас есть что-то полностью за пределами дерева наследования, которое также может ходить, например, робот. Робот будет реализовывать IWalkable, но, вероятно, не будет производным от Animal. Если вы хотите список вещей, которые могут ходить,
Теперь замените IWalkable чем-то более программным, таким как IPersistable, и аналогия станет намного ближе к тому, что вы увидите в реальной программе.
источник
Используйте интерфейсы, когда реализации одной и той же функциональности будут отличаться.
Используйте абстрактные / базовые классы, когда вам нужно поделиться общей конкретной реализацией.
источник
is
ключевое слово)Думайте об интерфейсе как о контракте. Это способ сказать: «Эти классы должны следовать этим правилам».
Таким образом, в примере с IAnimal это способ сказать: «Я ДОЛЖЕН иметь возможность вызывать Run, Walk и т. Д. Для классов, которые реализуют IAnimal».
Почему это полезно? Возможно, вы захотите создать функцию, основанную на том факте, что вы должны иметь возможность вызывать Run и Walk, например, для объекта. Вы могли бы иметь следующее:
... и повторите это для всех объектов, которые вы знаете, могут бегать и ходить. Однако с вашим интерфейсом IAnimal вы можете определить функцию один раз следующим образом:
Программируя интерфейс, вы по существу доверяете классам для реализации намерений интерфейса. Так что в нашем примере мысль «Мне все равно, как они бегают и ходят, если они бегут и ходят. Мой RunThenWalk будет действителен до тех пор, пока они выполняют это соглашение. Он прекрасно функционирует, ничего не зная о класс."
В этом связанном вопросе также есть хорошая дискуссия .
источник
Не волнуйся так сильно. Многим разработчикам, редко понадобится написать интерфейс. Вы будете часто использовать интерфейсы, доступные в .NET Framework, но если вы не чувствуете необходимости писать их в ближайшее время, в этом нет ничего удивительного.
Я всегда привожу пример, если у вас есть класс Sailboat и класс Viper. Они наследуют класс Boat и класс Car соответственно. Теперь скажите, что вам нужно перебрать все эти объекты и вызвать их
Drive()
метод. Хотя вы могли бы написать некоторый код, подобный следующему:Было бы намного проще написать:
источник
Джимми прав, когда вы хотите иметь возможность использовать одну переменную для нескольких типов, но все эти типы реализуют один и тот же метод через объявление интерфейса. Затем вы можете вызвать их основной метод в типизированной переменной интерфейса.
Однако есть и вторая причина использовать интерфейсы. Когда архитектор проекта - другой человек, а не кодировщик реализации, или есть несколько кодировщиков реализации и один руководитель проекта. Ответственное лицо может написать целую связку интерфейсов и увидеть, что система взаимодействует, а затем предоставить разработчикам возможность заполнять интерфейсы классами реализации. Это лучший способ убедиться, что несколько человек пишут совместимые классы, и они могут делать это параллельно.
источник
Мне нравится армейская аналогия.
Сержанту все равно, являетесь ли вы разработчиком программного обеспечения , музыкантом или юристом .
С тобой обращаются как с солдатом .
Это проще для сержанта не заморачиваться с конкретными деталями лиц , с которыми он работает с,
относиться ко всем как солдат абстракций (... и наказать их , когда они не в состоянии действовать , как из них).
Способность людей вести себя как солдат называется полиморфизмом.
Интерфейсы - это программные конструкции, которые помогают достичь полиморфизма.
Необходимость абстрагировать детали для достижения простоты - это ответ на Ваш вопрос.
источник
По моему опыту, движущей силы для создания интерфейсов не возникало, пока я не начал проводить модульное тестирование с помощью фреймворка. Стало совершенно очевидно, что использование интерфейсов значительно облегчит работу по моделированию (поскольку среда зависит от виртуальных методов). Когда я начал, я понял, насколько важно абстрагировать интерфейс от моего класса от реализации. Даже если я не создаю реальный интерфейс, я пытаюсь сделать мои методы виртуальными (предоставляя неявный интерфейс, который можно переопределить).
Есть много других причин, которые я нашел, чтобы укрепить хорошую практику рефакторинга для интерфейсов, но именно юнит-тестирование / насмешка были тем, что обеспечило первый «ага момент» практического опыта.
РЕДАКТИРОВАТЬ : Чтобы уточнить, с модульным тестированием и макетом у меня всегда есть две реализации - реальная, конкретная реализация и альтернативная реализация макета, используемая в тестировании. Когда у вас есть две реализации, значение интерфейса становится очевидным - разберитесь с ним с точки зрения интерфейса, чтобы вы могли заменить реализацию в любое время. В этом случае я заменяю его фиктивным интерфейсом. Я знаю, что могу сделать это без реального интерфейса, если мой класс построен правильно, но использование реального интерфейса усиливает это и делает его чище (понятнее читателю). Без этого стимула я не думаю, что оценил бы ценность интерфейсов, поскольку большинство моих классов когда-либо имели единственную конкретную реализацию.
источник
Некоторые примеры, не связанные с программированием, которые могут помочь вам увидеть правильное использование интерфейсов в программировании.
Есть интерфейс между электрическими устройствами и электрической сетью - это набор соглашений о форме вилок и розеток и напряжениях / токах на них. Если вы хотите внедрить новое электрическое устройство, пока ваш штекер следует правилам, он сможет получать услуги из сети. Это делает расширяемость очень простой и устраняет или снижает затраты на координацию : вам не нужно уведомлять поставщика электроэнергии о том, как работает ваше новое устройство, и приходить к отдельному соглашению о том, как подключить ваше новое устройство к сети.
Страны имеют стандартные железнодорожные колеи. Это позволяет разделить рабочую силу между инжиниринговыми компаниями, которые устанавливают рельсы, и инжиниринговыми компаниями, которые строят поезда, для работы на этих рельсах, и это позволяет железнодорожным компаниям заменять и модернизировать поезда без реорганизации всей системы.
Услуга, которую бизнес представляет клиенту, может быть описана как интерфейс: четко определенный интерфейс подчеркивает услугу и скрывает средства . Когда вы помещаете письмо в почтовый ящик, вы ожидаете, что почтовая система доставит письмо в течение определенного времени, но у вас нет никаких ожиданий относительно того, как письмо доставлено: вам не нужно знать об этом , и у почтовой службы есть возможность выбрать способ доставки, который наилучшим образом соответствует требованиям и текущим обстоятельствам. Исключением является способность клиентов выбирать авиапочту - это не тот интерфейс, который разработал бы современный компьютерный программист, поскольку он раскрывает слишком много реализации.
Примеры от природы: я не слишком увлечен примерами eats (), makeSound (), move () и т. Д. Они действительно описывают поведение, которое является правильным, но они не описывают взаимодействия и то, как они включены . Очевидные примеры интерфейсов, которые обеспечивают взаимодействие в природе, связаны с размножением, например, цветок обеспечивает определенный интерфейс для пчелы, так что опыление может иметь место.
источник
Вполне возможно, что вы будете работать как разработчик .net и никогда не будете писать свои собственные интерфейсы. В конце концов, мы десятилетиями прекрасно жили без них, а наши языки были по-прежнему тьюрингово-полными.
Я не могу сказать вам, почему вам нужны интерфейсы, но я могу дать вам список того, где мы используем их в нашем текущем проекте:
В нашей модели подключаемых модулей мы загружаем подключаемые модули по интерфейсу и предоставляем этот интерфейс создателям подключаемых модулей.
В нашей системе обмена сообщениями между машинами все классы сообщений реализуют определенный интерфейс и «развертываются» с помощью интерфейса.
Наша система управления конфигурацией определяет интерфейс, используемый для установки и получения настроек конфигурации.
У нас есть один интерфейс, который мы используем, чтобы избежать неприятных циклических ссылок. (Не делайте этого, если вам не нужно.)
Я предполагаю, что если есть правило, это использовать интерфейсы, когда вы хотите сгруппировать несколько классов в пределах отношения is-a, но вы не хотите предоставлять какую-либо реализацию в базовом классе.
источник
Пример кода (комбинация Эндрю с моим дополнительным в интерфейсе « что такое цель» ), который также объясняет, почему интерфейс вместо абстрактного класса в языках без поддержки множественного наследования (c # и Ява):
Обратите внимание, что FileLogger и DataBaseLogger не нуждаются в интерфейсе (это может быть абстрактный базовый класс Logger). Но учтите, что вы обязаны использовать сторонний регистратор, который заставляет вас использовать базовый класс (допустим, он предоставляет защищенные методы, которые вам нужно использовать). Поскольку язык не поддерживает множественное наследование, вы не сможете использовать подход абстрактного базового класса.
Итог: используйте интерфейс, когда это возможно, чтобы получить дополнительную гибкость в вашем коде. Ваша реализация менее привязана, поэтому она лучше подходит для изменений.
источник
Я использовал интерфейсы время от времени, и вот мое последнее использование (имена были обобщены):
У меня есть несколько пользовательских элементов управления в WinForm, которые должны сохранять данные в моем бизнес-объекте. Один из подходов - вызывать каждый элемент управления отдельно:
Проблема с этой реализацией состоит в том, что каждый раз, когда я добавляю элемент управления, мне нужно перейти в метод «Сохранить данные» и добавить новый элемент управления.
Я изменил свои элементы управления, чтобы реализовать интерфейс ISaveable, который имеет метод SaveToBusinessObject (...), поэтому теперь мой метод «Сохранить данные» просто перебирает элементы управления, и если он находит тот, который является ISaveable, он вызывает SaveToBusinessObject. Поэтому теперь, когда требуется новый элемент управления, все, что нужно сделать, это реализовать ISaveable в этом объекте (и никогда не трогать другой класс).
Часто нереализованное преимущество интерфейсов заключается в том, что вы локализуете модификации. После определения вы редко будете изменять общий поток приложения, но вы будете часто вносить изменения на уровне детализации. Когда вы сохраняете детали в определенных объектах, изменение в ProcessA не повлияет на изменение в ProcessB. (Базовые занятия также дают вам это преимущество.)
РЕДАКТИРОВАТЬ: Еще одно преимущество является специфика в действиях. Как и в моем примере, все, что я хочу сделать, это сохранить данные; Мне все равно, какой это элемент управления или может ли он делать что-то еще - я просто хочу знать, смогу ли я сохранить данные в элементе управления. Это делает мой код сохранения довольно ясным - нет никаких проверок, чтобы увидеть, текстовый он, числовой, логический или какой-либо еще, потому что все это выполняет пользовательский элемент управления.
источник
Вы должны определить интерфейс, как только вам нужно вызвать поведение для вашего класса.
Поведение животного может включать ходьбу, еду, бег и т. Д. Поэтому вы определяете их как интерфейсы.
Другим практическим примером является интерфейс ActionListener (или Runnable). Вы будете реализовывать их, когда вам нужно отслеживать конкретное событие. Следовательно, вам необходимо предоставить реализацию
actionPerformed(Event e)
метода в вашем классе (или подклассе). Аналогично, для интерфейса Runnable вы предоставляете реализацию дляpublic void run()
метода.Кроме того, эти интерфейсы могут быть реализованы любым количеством классов.
Другим примером использования интерфейсов (в Java) является реализация множественного наследования, предлагаемого в C ++.
источник
Предположим, вы хотите смоделировать раздражения, которые могут возникнуть при попытке заснуть.
Модель до интерфейсов
Как вы ясно видите, многие «вещи» могут раздражать, когда вы пытаетесь уснуть.
Использование классов без интерфейсов
Но когда дело доходит до использования этих классов, у нас есть проблема. У них нет ничего общего. Вы должны вызывать каждый метод отдельно.
Модель с интерфейсами
Чтобы преодолеть эту проблему, мы можем ввести интерфейс
И реализовать это внутри классов
Использование с интерфейсами
Что сделает использование этих классов намного проще
источник
Neighbour
аLampJustOutsideYourWindow
также нужно реализоватьAnnoying
?Самый простой пример - что-то вроде платежных процессоров (Paypal, PDS и т. Д.).
Допустим, вы создали интерфейс IPaymentProcessor, который имеет методы ProcessACH и ProcessCreditCard.
Теперь вы можете реализовать конкретную реализацию Paypal. Создание этих методов вызывает специфические функции PayPal.
Если позже вы решите, что вам нужно переключиться на другого поставщика, вы можете. Просто создайте еще одну конкретную реализацию для нового провайдера. Поскольку все, к чему вы привязаны, это ваш интерфейс (контракт), вы можете поменять, какой из них использует ваше приложение, не изменяя код, который его использует.
источник
Это также позволяет вам выполнять Mock модульное тестирование (.Net). Если ваш класс использует интерфейс, вы можете смоделировать объект в модульном тестировании и легко проверить логику (фактически не затрагивая базу данных, веб-службу и т. Д.).
http://www.nmock.org/
источник
Если вы просматриваете сборки .NET Framework и углубляетесь в базовые классы для любого из стандартных объектов, вы заметите много интерфейсов (членов, названных ISomeName).
Интерфейсы в основном для реализации каркасов, больших или маленьких. Я чувствовал то же самое в отношении интерфейсов, пока не захотел написать собственную структуру. Я также обнаружил, что понимание интерфейсов помогло мне быстрее изучить фреймворки. В тот момент, когда вы захотите написать более элегантное решение для чего угодно, вы обнаружите, что интерфейс имеет большой смысл. Это как метод дать классу надеть подходящую одежду для работы. Что еще более важно, интерфейсы позволяют системам становиться намного более самодокументируемыми, потому что сложные объекты становятся менее сложными, когда класс реализует интерфейсы, что помогает классифицировать его функциональные возможности.
Классы реализуют интерфейсы, когда они хотят иметь возможность участвовать в платформе явно или неявно. Например, IDisposable - это общий интерфейс, который предоставляет сигнатуру метода для популярного и полезного метода Dispose (). В каркасе все, что вам или другому разработчику необходимо знать о классе, это то, что если он реализует IDisposable, то вы знаете, что ((IDisposable) myObject) .Dispose () доступен для вызова в целях очистки.
КЛАССИЧЕСКИЙ ПРИМЕР: без реализации интерфейса IDisposable вы не можете использовать конструкцию ключевого слова using () в C #, поскольку для этого требуется, чтобы любой объект, указанный в качестве параметра, мог быть неявно приведен к IDisposable.
СЛОЖНЫЙ ПРИМЕР. Более сложный пример - класс System.ComponentModel.Component. Этот класс реализует как IDisposable, так и IComponent. Большинство, если не все, объекты .NET, с которыми связан визуальный конструктор, реализуют IComponent, чтобы среда IDE могла взаимодействовать с компонентом.
ЗАКЛЮЧЕНИЕ. По мере знакомства с .NET Framework первое, что вы сделаете, столкнувшись с новым классом в обозревателе объектов или в инструменте .NET Reflector (бесплатно) ( http://www.red-gate.com). / products / рефлектор / ) должен проверить, какой класс он наследует, а также интерфейсы, которые он реализует. .NET Reflector даже лучше, чем Object Browser, потому что он позволяет вам также видеть классы Derived. Это позволяет вам узнать обо всех объектах, которые являются производными от определенного класса, тем самым потенциально узнавая о существующей функциональности фреймворка. Это особенно важно, когда в .NET Framework добавлены обновленные или новые пространства имен.
источник
Представьте, что вы делаете игру-стрелялку от первого лица. У игрока есть несколько видов оружия на выбор.
У нас может быть интерфейс,
Gun
который определяет функциюshoot()
.Нам нужны разные подклассы
Gun
класса, а именноShotGun
Sniper
и так далее.Класс стрелка
Стрелок имеет все оружие в своей броне. Давайте создадим его
List
для представления.Стрелок циклически перебирает оружие, используя функцию
switchGun()
Мы можем установить текущий Gun, используя вышеуказанную функцию и просто вызвать
shoot()
функцию, когдаfire()
вызывается.Поведение функции съемки будет варьироваться в зависимости от различных реализаций
Gun
интерфейса.Вывод
Создайте интерфейс, когда функция класса зависит от функции из другого класса , которая может изменить свое поведение, основываясь на экземпляре (объекте) реализованного класса.
Например,
fire()
функция изShooter
класса ожидает, что guns (Sniper
,ShotGun
) реализуетshoot()
функцию. Так что, если мы переключим пистолет и огонь.Мы изменили поведение
fire()
функции.источник
Чтобы расширить то, что сказал Ларсенал. Интерфейс - это контракт, которому должны следовать все реализующие классы. Из-за этого вы можете использовать технику, называемую программированием контракта. Это позволяет вашему программному обеспечению стать независимым от реализации.
источник
Интерфейсы обычно используются, когда вы хотите определить поведение, которое могут демонстрировать объекты.
Хорошим примером этого в мире .NET является IDisposable интерфейс , который используется в любых классах Microsoft, использующих системные ресурсы, которые должны быть освобождены вручную. Требуется, чтобы реализующий его класс имел метод Dispose ().
(Метод Dispose () также вызывается с помощью языковой конструкции using для VB.NET и C # , которая работает только на
IDisposable
s)Помните, что вы можете проверить, реализует ли объект определенный интерфейс, используя такие конструкции, как
TypeOf ... Is
(VB.NET),is
(C #),instanceof
(Java) и т. Д.источник
Как, возможно, уже ответили несколько человек, интерфейсы могут использоваться для обеспечения определенных поведений между классами, которые не будут реализовывать эти поведения одинаково. Итак, реализуя интерфейс, вы говорите, что ваш класс имеет поведение интерфейса. Интерфейс IAnimal не будет типичным интерфейсом, потому что классы Dog, Cat, Bird и т. Д. Являются типами животных и, вероятно, должны расширять его, что является случаем наследования. Вместо этого интерфейс в этом случае будет больше похож на поведение животных, например, IRunnable, IFlyable, ITrainable и т. Д.
Интерфейсы хороши для многих вещей, одной из ключевых является возможность подключения. Например, объявление метода, имеющего параметр List, позволит передавать все, что реализует интерфейс List, что позволяет разработчику позже удалить и подключить другой список без необходимости переписывать тонну кода.
Возможно, вы никогда не будете использовать интерфейсы, но если вы разрабатываете проект с нуля, особенно какой-то фреймворк, вы, вероятно, захотите ознакомиться с ними.
Я бы рекомендовал прочитать главу об интерфейсах в Java Design от Coad, Mayfield и Kern. Они объясняют это немного лучше, чем обычный вводный текст. Если вы не используете Java, вы можете просто прочитать начало главы, которая в основном представляет собой концепцию.
источник
Как любая техника программирования, которая добавляет гибкость вашей системе, интерфейсы также добавляют некоторый уровень сложности. Они часто великолепны, и вы можете использовать их повсюду (вы можете создать интерфейс для всех ваших классов) - но при этом вы создадите более сложную систему, которую будет сложнее поддерживать.
Здесь, как обычно, есть компромисс: гибкость по сравнению с ремонтопригодностью. Какой из них важнее? Там нет ответов - это зависит от проекта. Но только помните, что каждое программное обеспечение должно быть поддержано ...
Поэтому мой совет: не используйте интерфейсы, пока они вам действительно не понадобятся. (В Visual Studio вы можете извлечь интерфейс из существующего класса за 2 секунды, поэтому не спешите.)
Сказав это, когда вам нужно создать интерфейс?
Я делаю это, когда я рефакторинг метода, который вдруг должен обрабатывать два или более одинаковых классов. Затем я создаю интерфейс, назначаю этот интерфейс двум (или более) подобным классам и меняю тип параметра метода (заменяю тип класса на тип интерфейса).
И это работает: о)
Единственное исключение: когда я, когда имитировать объекты, интерфейс гораздо проще в использовании. Поэтому я часто создаю интерфейс только для этого.
PS: когда я пишу «интерфейс», я имею в виду: «интерфейс любого базового класса», включая чистые классы интерфейса. Обратите внимание, что абстрактные классы часто лучше, чем чистые интерфейсы, поскольку вы можете добавить к ним логику.
С уважением, Сильвен.
источник
Интерфейсы станут очевидными, когда вы станете разработчиком библиотеки (кто-то, кто кодирует для других программистов). Большинство из нас начинают как разработчики приложений , где мы используем существующие API и библиотеки программирования.
Наряду с тем, что Интерфейсы являются контрактом , еще никто не упомянул, что Интерфейсы - отличный способ сделать некоторые части вашего кода стабильными . Это особенно полезно, когда это командный проект (или когда вы разрабатываете код, используемый другими разработчиками). Итак, вот конкретный сценарий для вас:
Также посмотрите этот связанный вопрос о кодировании интерфейса .
источник
Есть так много целей для использования интерфейса.
Использование в полиморфном поведении. Где вы хотите вызвать конкретные методы дочернего класса с интерфейсом, имеющим ссылку на дочерний класс.
Наличие контракта с классами для реализации всех методов там, где это необходимо, как обычно используется с объектами COM, где класс-оболочка генерируется на DLL, которая наследует интерфейс; эти методы вызываются за кулисами, и вам просто нужно реализовать их, но с той же структурой, которая определена в COM DLL, которую вы можете узнать только через интерфейс, который они предоставляют.
Сократить использование памяти путем загрузки определенных методов в классе. Например, если у вас есть три бизнес-объекта, и они реализованы в одном классе, вы можете использовать три интерфейса.
Например, IUser, IOrder, IOrderItem
Если вы хотите добавить только пользователя, сделайте так:
источник