Причина интерфейсов действительно ускользает от меня. Насколько я понимаю, это своего рода обходной путь для несуществующего мульти-наследования, которого нет в C # (или мне так сказали).
Все, что я вижу, это то, что вы заранее определяете некоторые члены и функции, которые затем должны быть заново определены в классе. Таким образом, делая интерфейс избыточным. Это просто похоже на синтаксис… ну, хлам для меня (пожалуйста, не обижайся, это значит. Хлам как в бесполезных вещах).
В приведенном ниже примере, взятом из другого потока интерфейсов C # при переполнении стека, я бы просто создал базовый класс с именем Pizza вместо интерфейса.
простой пример (взят из другого вклада переполнения стека)
public interface IPizza
{
public void Order();
}
public class PepperoniPizza : IPizza
{
public void Order()
{
//Order Pepperoni pizza
}
}
public class HawaiiPizza : IPizza
{
public void Order()
{
//Order HawaiiPizza
}
}
struct
типов.Ответы:
Дело в том, что интерфейс представляет собой контракт . Набор открытых методов, которые должен иметь любой реализующий класс. Технически интерфейс управляет только синтаксисом, т. Е. Какие методы существуют, какие аргументы они получают и что они возвращают. Обычно они также инкапсулируют семантику, хотя только в документации.
Затем вы можете иметь различные реализации интерфейса и менять их по своему желанию. В вашем примере, поскольку каждый экземпляр пиццы является экземпляром, который
IPizza
вы можете использоватьIPizza
везде, где вы обрабатываете экземпляр неизвестного типа пиццы. Любой экземпляр, тип которого наследуется,IPizza
гарантированно будет упорядоченным, поскольку у него естьOrder()
метод.Python не является статически типизированным, поэтому типы сохраняются и проверяются во время выполнения. Таким образом, вы можете попробовать вызвать
Order()
метод для любого объекта. Среда выполнения счастлива, если у объекта есть такой метод, и, вероятно, просто пожимает плечами и говорит «Мех», если его нет. Не так в C #. Компилятор отвечает за правильные вызовы, и если у него есть какой-то случайный случай,object
компилятор еще не знает, будет ли экземпляр во время выполнения иметь этот метод. С точки зрения компилятора он недействителен, поскольку не может это проверить. (Вы можете делать такие вещи с помощью рефлексии илиdynamic
ключевого слова, но сейчас это немного далеко, я думаю.)Также обратите внимание, что интерфейс в обычном смысле не обязательно должен быть C #
interface
, это может быть также абстрактный класс или даже обычный класс (который может пригодиться, если все подклассы должны совместно использовать некоторый общий код - в большинстве случаев Впрочем,interface
хватит).источник
foo
реализуетIWidget
, тогда программист, увидевший вызов,foo.woozle()
может посмотреть документациюIWidget
и узнать, что должен делать этот метод . Программист может не знать, откуда поступит код для фактической реализации, но любой тип, соответствующийIWidget
контракту интерфейса, будет реализованfoo
в соответствии с этим контрактом. В динамическом языке, напротив, не было бы четкого ориентира для того, чтоfoo.woozle()
должно означать.Никто толком не объяснил, насколько полезны интерфейсы, поэтому я собираюсь дать ему шанс (и немного украду идею из ответа Шамима).
Давайте рассмотрим идею заказа пиццы. Вы можете иметь несколько видов пиццы, и общее действие для каждой пиццы готовит заказ в системе. Каждая пицца должна быть приготовлена, но каждая пицца готовится по-разному . Например, когда заказывается пицца с начинкой из корочки, система, вероятно, должна проверить наличие в ресторане определенных ингредиентов и отложить те, которые не нужны для пиццы с глубоким блюдом.
При написании этого в коде, технически вы могли бы просто сделать
Однако для пиццы с глубоким блюдом (в терминах C #) может потребоваться установить в
Prepare()
методе другие свойства, нежели фаршированная корка, и, таким образом, вы получите множество дополнительных свойств, и класс не будет хорошо масштабироваться (что, если вы добавите новый виды пиццы).Правильный способ решить эту проблему - использовать интерфейс. Интерфейс заявляет, что все пиццы могут быть приготовлены, но каждая пицца может быть приготовлена по-разному. Так что если у вас есть следующие интерфейсы:
Теперь вашему коду обработки заказа не нужно точно знать, какие виды пиццы были заказаны для обработки ингредиентов. Это просто имеет:
Даже если каждый тип пиццы готовится по-разному, эта часть кода не должна заботиться о том, с каким типом пиццы мы имеем дело, она просто знает, что она вызывается для пиццы, и поэтому каждый вызов
Prepare
автоматически готовит каждую пиццу правильно на основе его типа, даже если коллекция имеет несколько видов пиццы.источник
public
. Так что внутри интерфейса это должно быть простоvoid Prepare();
Для меня, когда я начинал, смысл этого стал ясен только тогда, когда вы перестали смотреть на них как на вещи, которые делают ваш код легче / быстрее писать - это не их цель. У них есть ряд применений:
(Это приведет к потере аналогии с пиццей, так как это не очень легко визуализировать)
Скажем, вы делаете простую игру на экране, и в ней будут существа, с которыми вы взаимодействуете.
О: Они могут упростить поддержку вашего кода в будущем, введя слабую связь между вашим внешним интерфейсом и вашей серверной реализацией.
Вы можете написать это для начала, так как будут только тролли:
Внешний интерфейс:
Спустя две недели, маркетологи решили, что вам также нужны орки, так как они читают о них в твиттере, поэтому вам придется сделать что-то вроде:
Внешний интерфейс:
И вы можете видеть, как это начинает становиться грязным. Здесь вы можете использовать интерфейс, чтобы ваш интерфейс был написан один раз и (вот важный момент) протестирован, а затем вы можете подключить дополнительные элементы интерфейса по мере необходимости:
Тогда передний конец:
Внешний интерфейс теперь заботится только об интерфейсе ICreature - он не беспокоится о внутренней реализации тролля или орка, а только о том, что они реализуют ICreature.
При рассмотрении этого с этой точки зрения важно отметить, что вы могли бы также легко использовать класс абстрактных существ, и с этой точки зрения это имеет тот же эффект.
И вы можете извлечь творение на фабрику:
И тогда наш интерфейс станет:
Внешний интерфейс теперь даже не должен иметь ссылку на библиотеку, в которой реализованы Troll и Orc (при условии, что фабрика находится в отдельной библиотеке) - ему вообще ничего не нужно знать о них.
B: Скажем, у вас есть функциональность, которую будут иметь только некоторые существа в вашей однородной структуре данных , например
Тогда передний конец может быть:
C: Использование для внедрения зависимости
С большинством инфраструктур внедрения зависимостей легче работать, когда существует очень слабая связь между кодом внешнего интерфейса и реализацией внутреннего интерфейса. Если мы возьмем наш фабричный пример выше и заставим нашу фабрику реализовать интерфейс:
Наш внешний интерфейс может затем внедрить это (например, контроллер MVC API) через конструктор (обычно):
С нашей структурой DI (например, Ninject или Autofac) мы можем настроить их так, чтобы во время выполнения экземпляр CreatureFactory создавался всякий раз, когда в конструкторе требуется ICreatureFactory - это делает наш код красивым и простым.
Это также означает, что когда мы пишем модульный тест для нашего контроллера, мы можем предоставить фиктивную ICreatureFactory (например, если конкретная реализация требует доступа к БД, мы не хотим, чтобы наши модульные тесты зависели от этого) и легко протестировать код в нашем контроллере ,
D: Есть и другие варианты использования, например, у вас есть два проекта A и B, которые по «устаревшим» причинам не имеют четкой структуры, и A имеет ссылку на B.
Затем вы обнаружите функциональность в B, которая должна вызывать метод уже в A. Вы не можете сделать это, используя конкретные реализации, когда получаете циклическую ссылку.
Вы можете иметь интерфейс, объявленный в B, который затем реализует класс в A. Вашему методу в B может быть передан экземпляр класса, который без проблем реализует интерфейс, даже если конкретный объект имеет тип в A.
источник
Вот ваши примеры объяснены:
источник
Приведенные выше примеры не имеют особого смысла. Вы можете выполнить все приведенные выше примеры, используя классы (абстрактный класс, если вы хотите, чтобы он вел себя только как контракт ):
Вы получаете то же поведение, что и с интерфейсом. Вы можете создать
List<Food>
и повторять это без знания того, какой класс находится сверху.Более адекватным примером будет множественное наследование:
Тогда вы можете использовать их как
MenuItem
и не заботиться о том, как они обрабатывают каждый вызов метода.источник
Простое объяснение с аналогией
Проблема для решения: какова цель полиморфизма?
Аналогия: Итак, я работница на стройке.
Ремесленники все время гуляют на стройке. Я не знаю, кто будет проходить через эти двери. Но я в основном говорю им, что делать.
Проблема с вышеуказанным подходом заключается в том, что я должен: (i) знать, кто ходит в эту дверь, и в зависимости от того, кто это, я должен сказать им, что делать. Это означает, что я должен знать все о конкретной сделке. Есть затраты / выгоды, связанные с этим подходом:
Последствия знания того, что делать:
Это означает, что если код плотника изменится с:
BuildScaffolding()
наBuildScaffold()
(т. Е. Небольшое изменение имени), тогда мне также придется изменить вызывающий класс (т. Е.Foreperson
Класс) - вам придется внести два изменения в код вместо (в основном) ) только один. С полиморфизмом вам (в основном) нужно всего лишь сделать одно изменение, чтобы достичь того же результата.Во-вторых, вам не придется постоянно спрашивать: кто вы? хорошо, сделай это ... кто ты? хорошо, сделайте это ..... полиморфизм - он сушит этот код и очень эффективен в определенных ситуациях:
с полиморфизмом вы можете легко добавлять дополнительные классы торговцев без изменения какого-либо существующего кода. (т.е. второй из принципов проектирования SOLID: принцип Open-Close).
Решение
Представьте себе сценарий, в котором, независимо от того, кто входит в дверь, я могу сказать: «Работа ()», и они выполняют свою работу по уважению, в которой они специализируются: сантехник будет заниматься трубами, а электрик - проводами.
Преимущество этого подхода заключается в том, что: (i) мне не нужно точно знать, кто входит в эту дверь - все, что мне нужно знать, это то, что они будут своего рода обычным делом и что они могут выполнять работу, и, во-вторых, (ii) мне не нужно ничего знать об этой конкретной сделке. Об этом позаботится традиция.
Итак, вместо этого:
Я могу сделать что-то вроде этого:
В чем выгода?
Преимущество заключается в том, что если конкретные требования к работе плотника и т. Д. Изменятся, то руководителю не нужно будет менять свой код - ему не нужно знать или заботиться. Все, что имеет значение, так это то, что плотник знает, что подразумевается под Work (). Во-вторых, если на стройплощадку приходит новый тип строителя, то бригадир не должен ничего знать о торговле - все, что беспокоит бригадира, это если строительный рабочий (например, Welder, Glazier, Tiler и т. Д.) Может получить некоторую работу ().
Иллюстрированная проблема и решение (с интерфейсами и без):
Нет интерфейса (пример 1):
Нет интерфейса (пример 2):
С интерфейсом:
Резюме
Интерфейс позволяет вам заставить человека выполнять работу, для которой он назначен, без знания того, кем он является, или специфики того, что он может делать. Это позволяет вам легко добавлять новые типы (торговли) без изменения существующего кода (технически вы действительно меняете его чуть-чуть), и это реальное преимущество подхода ООП по сравнению с более функциональной методологией программирования.
Если вы не понимаете ничего из вышеперечисленного или если это не ясно, спросите в комментарии, и я постараюсь сделать ответ лучше.
источник
В отсутствие утиной типизации, которую вы можете использовать в Python, C # полагается на интерфейсы для предоставления абстракций. Если все зависимости класса были конкретными типами, вы не могли бы передать любой другой тип - используя интерфейсы, вы можете передать любой тип, который реализует интерфейс.
источник
Пример Pizza плохой, потому что вы должны использовать абстрактный класс, который обрабатывает порядок, а пицца должна просто переопределять тип пиццы, например.
Вы используете интерфейсы, когда у вас есть общее свойство, но ваши классы наследуются из разных мест, или когда у вас нет общего кода, который вы могли бы использовать. Например, это используемые вещи, которые можно утилизировать
IDisposable
, вы знаете, что они будут утилизированы, вы просто не знаете, что произойдет, когда они будут утилизированы.Интерфейс - это просто контракт, который говорит вам о том, что объект может делать, какие параметры и какие возвращаемые типы ожидать.
источник
Рассмотрим случай, когда вы не контролируете или не владеете базовыми классами.
Возьмите визуальные элементы управления, например, в .NET для Winforms все они наследуются от базового класса Control, который полностью определен в .NET Framework.
Предположим, вы занимаетесь созданием пользовательских элементов управления. Вы хотите создавать новые кнопки, текстовые поля, списки, сетки и многое другое, и вы хотите, чтобы у них всех были определенные функции, уникальные для вашего набора элементов управления.
Например, вам может понадобиться общий способ управления ими или общий способ управления локализацией.
В этом случае вы не можете «просто создать базовый класс», потому что если вы делаете это, вы должны переопределить все что относится к элементам управления.
Вместо этого вы перейдете к Button, TextBox, ListView, GridView и т. Д. И добавите свой код.
Но это создает проблему: как вы теперь можете определить, какие элементы управления являются «вашими», как вы можете создать некоторый код, который говорит: «Для всех элементов управления в моей форме установите тему на X».
Введите интерфейсы.
Интерфейсы - это способ посмотреть на объект, чтобы определить, что объект придерживается определенного контракта.
Вы должны создать «YourButton», спуститься с кнопки и добавить поддержку всех необходимых вам интерфейсов.
Это позволит вам написать код, подобный следующему:
Это было бы невозможно без интерфейсов, вместо этого вам пришлось бы писать такой код:
источник
В этом случае вы могли бы (и, вероятно, могли бы) просто определить базовый класс Pizza и наследовать от них. Однако есть две причины, по которым интерфейсы позволяют вам делать вещи, которые не могут быть достигнуты другими способами:
Класс может реализовывать несколько интерфейсов. Он просто определяет функции, которые должен иметь класс. Реализация ряда интерфейсов означает, что класс может выполнять несколько функций в разных местах.
Интерфейс может быть определен в более высокой области, чем класс или вызывающая сторона. Это означает, что вы можете отделить функциональность, отделить зависимость проекта и сохранить функциональность в одном проекте или классе, а также реализовать это в другом месте.
Одним из следствий 2 является то, что вы можете изменить используемый класс, просто требуя, чтобы он реализовал соответствующий интерфейс.
источник
Учтите, что вы не можете использовать множественное наследование в C #, а затем снова посмотрите на свой вопрос.
источник
Интерфейс = контракт, используется для слабой связи (см. GRASP ).
источник
Если я работаю над API для рисования фигур, я могу использовать DirectX, графические вызовы или OpenGL. Итак, я создам интерфейс, который абстрагирует мою реализацию от того, что вы называете.
Таким образом , вы вызываете фабричный метод:
MyInterface i = MyGraphics.getInstance()
. Затем у вас есть контракт, и вы знаете, какие функции вы можете ожидатьMyInterface
. Таким образом, вы можете позвонитьi.drawRectangle
илиi.drawCube
и знать, что если вы меняете одну библиотеку на другую, то функции поддерживаются.Это становится более важным, если вы используете Dependency Injection, так как в XML-файле вы можете заменить реализацию.
Таким образом, у вас может быть одна криптографическая библиотека, которую можно экспортировать для общего пользования, и другая, предназначенная для продажи только американским компаниям, и разница в том, что вы меняете файл конфигурации, а остальная часть программы не изменилось.
Это широко используется для коллекций в .NET, так как вы должны просто использовать, например,
List
переменные, и не волнуйтесь, был ли это ArrayList или LinkedList.Пока вы кодируете интерфейс, разработчик может изменить фактическую реализацию, а остальная часть программы останется неизменной.
Это также полезно при модульном тестировании, так как вы можете макетировать целые интерфейсы, поэтому мне не нужно переходить на базу данных, а на макетную реализацию, которая просто возвращает статические данные, поэтому я могу протестировать свой метод, не беспокоясь о том, база данных недоступна для обслуживания или нет.
источник
Интерфейс - это на самом деле контракт, которому должны следовать реализующие классы, фактически он является основой практически для каждого шаблона проектирования, который я знаю.
В вашем примере интерфейс создан, потому что тогда все, что IS A Pizza, что означает реализацию интерфейса Pizza, гарантированно будет реализовано
После упомянутого вами кода вы можете получить что-то вроде этого:
Таким образом, вы используете полиморфизм, и все, что вас волнует, это то, что ваши объекты реагируют на order ().
источник
Я выполнил поиск по слову «композиция» на этой странице и ни разу не увидел его. Этот ответ очень в дополнение к вышеупомянутым ответам.
Одна из абсолютно важных причин использования интерфейсов в объектно-ориентированном проекте заключается в том, что они позволяют вам отдавать предпочтение композиции по сравнению с наследованием. Реализуя интерфейсы, вы можете отделить свои реализации от различных алгоритмов, которые вы применяете к ним.
Этот превосходный учебник «Шаблон декоратора» Дерека Банаса (который, как ни странно, в качестве примера также используется пицца) является достойной иллюстрацией:
https://www.youtube.com/watch?v=j40kRwSm4VE
источник
Интерфейсы предназначены для применения связи между различными классами. например, у вас есть класс для автомобиля и дерева;
Вы хотите добавить выгорающую функциональность для обоих классов. Но у каждого класса есть свои способы сжигания. так что вы просто делаете;
источник
Вы получите интерфейсы, когда они вам понадобятся :) Вы можете изучать примеры, но вам нужен Ага! эффект, чтобы действительно получить их.
Теперь, когда вы знаете, что такое интерфейсы, просто используйте их без кода. Рано или поздно вы столкнетесь с проблемой, когда использование интерфейсов будет наиболее естественным.
источник
Я удивлен, что не много постов содержат одну самую важную причину для интерфейса: Шаблоны проектирования . Это более широкая картина использования контрактов, и, хотя это синтаксическое оформление машинного кода (если честно, компилятор, вероятно, просто игнорирует их), абстракция и интерфейсы имеют решающее значение для ООП, понимания человеком и сложных системных архитектур.
Давайте расширим аналогию с пиццей, чтобы сказать полноценное блюдо из 3 блюд. У нас все еще будет ядро
Prepare()
интерфейс для всех наших категорий продуктов питания, но у нас также будут абстрактные объявления для выбора блюд (закуска, основное блюдо, десерт) и различные свойства для типов продуктов (пикантная / сладкая, вегетарианская / невегетарианская, без глютена и т. д.).Основываясь на этих спецификациях, мы могли бы реализовать шаблон Abstract Factory для концептуализации всего процесса, но использовать интерфейсы, чтобы гарантировать, что только основы были конкретными. Все остальное может стать гибким или способствовать полиморфизму, но поддерживать инкапсуляцию между различными классами,
Course
которые реализуютICourse
интерфейс.Если бы у меня было больше времени, я хотел бы составить полный пример этого, или кто-то может расширить это для меня, но в заключение, интерфейс C # был бы лучшим инструментом при разработке систем такого типа.
источник
Вот интерфейс для объектов, которые имеют прямоугольную форму:
Все, что требуется, - это реализовать способы доступа к ширине и высоте объекта.
Теперь давайте определим метод, который будет работать с любым объектом
IRectangular
:Это вернет область любого прямоугольного объекта.
Давайте реализуем класс,
SwimmingPool
который является прямоугольным:И еще один класс,
House
который также является прямоугольным:Учитывая это, вы можете вызвать
Area
метод на домах или бассейнах:Таким образом, ваши классы могут «наследовать» поведение (static-методы) от любого количества интерфейсов.
источник
Интерфейс определяет договор между поставщиком определенной функциональности и соответствующими потребителями. Он отделяет реализацию от контракта (интерфейса). Вы должны взглянуть на объектно-ориентированную архитектуру и дизайн. Вы можете начать с Википедии: http://en.wikipedia.org/wiki/Interface_(computing)
источник
Какой ?
Интерфейсы - это в основном контракт, который реализуют все классы должны следовать Интерфейс. Они похожи на класс, но не имеют реализации.
В
C#
именах интерфейсов по соглашению определяется префикс «I», поэтому если вы хотите иметь интерфейс с именем shape, вы должны объявить его какIShapes
Теперь почему?
Improves code re-usability
Допустим , вы хотите сделать
Circle
,Triangle.
вы можете сгруппировать их вместе и называть их ,Shapes
и есть методы , чтобы привлечьCircle
иTriangle
но имеющие реализацию бетона будет плохая идея , потому что завтра вы можете решить, что еще 2Shapes
Rectangle
&Square
. Теперь, когда вы добавляете их, есть большая вероятность, что вы можете сломать другие части вашего кода.С интерфейсом вы изолируете различные реализации от контракта
Живой Сценарий День 1
Вас попросили создать приложение для рисования круга и треугольника
Живой Сценарий День 2
Если вас попросили добавить
Square
иRectangle
к нему, все, что вам нужно сделать, это создать для него добавлениеclass Square: IShapes
иMain
добавить в списокshapes.Add(new Square());
источник
Здесь есть много хороших ответов, но я хотел бы попробовать с немного другой точки зрения.
Возможно, вы знакомы с принципами SOLID объектно-ориентированного проектирования. В итоге:
S - Принцип единой ответственности O - Открытый / закрытый принцип L - Лисковский принцип замещения I - Принцип разделения интерфейса D - Принцип инверсии зависимостей
Следование принципам SOLID помогает создавать код, который будет чистым, хорошо продуманным, связным и слабо связанным При условии:
тогда все, что помогает в управлении зависимостями, является большой победой. Интерфейсы и принцип инверсии зависимостей действительно помогают отделить код от зависимостей от конкретных классов, поэтому код можно написать и обосновать с точки зрения поведения а не реализации. Это помогает разбить код на компоненты, которые могут быть скомпонованы во время выполнения, а не во время компиляции, а также означает, что эти компоненты могут быть легко подключены и отключены без изменения остальной части кода.
Интерфейсы помогают, в частности, с принципом инверсии зависимости, где код может быть объединен в набор служб, причем каждая служба описывается интерфейсом. Затем сервисы могут быть «внедрены» в классы во время выполнения, передав их в качестве параметра конструктора. Эта методика действительно становится критической, если вы начинаете писать модульные тесты и используете тестовую разработку. Попытайся! Вы быстро поймете, как интерфейсы помогают разбить код на управляемые куски, которые можно тестировать отдельно.
источник
Основное назначение интерфейсов заключается в том, что он заключает договор между вами и любым другим классом, который реализует этот интерфейс, что делает ваш код отделенным и обеспечивает расширяемость.
источник
Там есть действительно хорошие примеры.
Другой, в случае оператора switch, вам больше не нужно обслуживать и переключать каждый раз, когда вы хотите, чтобы rio выполнял задачу определенным образом.
В вашем примере с пиццей, если вы хотите сделать пиццу, интерфейс - это все, что вам нужно, оттуда каждая пицца заботится о своей собственной логике.
Это помогает уменьшить сцепление и цикломатическую сложность. Вы все еще должны реализовать логику, но в более широкой картине вам придется отслеживать меньше.
Для каждой пиццы вы можете отслеживать информацию, относящуюся к этой пицце. То, что есть у других пицц, не имеет значения, потому что только другие пиццы должны знать.
источник
Самый простой способ думать об интерфейсах - это понять, что означает наследование. Если класс CC наследует класс C, это означает, что:
Эти две функции наследования в некотором смысле независимы; хотя наследование применяется оба одновременно, также возможно применить второе без первого. Это полезно, потому что позволить объекту наследовать члены от двух или более несвязанных классов намного сложнее, чем разрешить замену одного типа вещей несколькими типами.
Интерфейс чем-то напоминает абстрактный базовый класс, но с ключевым отличием: объект, который наследует базовый класс, не может наследовать ни один другой класс. Напротив, объект может реализовывать интерфейс, не влияя на его способность наследовать любой желаемый класс или реализовывать любые другие интерфейсы.
Хорошая особенность этого (недостаточно используется в .net framework, IMHO) заключается в том, что они позволяют декларативно указывать, что может делать объект. Некоторым объектам, например, понадобится объект источника данных, из которого они могут извлекать вещи по индексу (как это возможно в List), но им не нужно ничего там хранить. Другие подпрограммы будут нуждаться в объекте хранилища данных, где они могут хранить вещи не по индексу (как в Collection.Add), но им не нужно будет что-либо читать обратно. Некоторые типы данных разрешают доступ по индексу, но не разрешают запись; другие разрешат запись, но не разрешат доступ по индексу. Некоторые, конечно, позволят и то и другое.
Если бы ReadableByIndex и Appendable были несвязанными базовыми классами, было бы невозможно определить тип, который можно было бы передать как вещам, ожидающим ReadableByIndex, так и вещам, ожидающим Appendable. Можно попытаться смягчить это, используя ReadableByIndex или Appendable, производные от другого; производный класс должен был бы сделать доступными общедоступные члены для обеих целей, но предупредите, что некоторые общедоступные члены могут фактически не работать. Некоторые из классов и интерфейсов Microsoft делают это, но это довольно странно. Более чистый подход состоит в том, чтобы иметь интерфейсы для различных целей, а затем объекты должны реализовывать интерфейсы для того, что они действительно могут делать. Если один имел интерфейс IReadableByIndex, а другой интерфейс IAppendable,
источник
Интерфейсы также могут быть последовательно соединены для создания еще одного интерфейса. Эта возможность реализации нескольких интерфейсов дает разработчику преимущество добавления функциональности в их классы без необходимости изменения функциональности текущего класса (принципы SOLID)
O = "Классы должны быть открыты для расширения, но закрыты для модификации"
источник
Для меня преимущество / преимущество интерфейса в том, что он более гибкий, чем абстрактный класс. Поскольку вы можете наследовать только 1 абстрактный класс, но можете реализовать несколько интерфейсов, изменения в системе, которая наследует абстрактный класс во многих местах, становятся проблематичными. Если оно унаследовано в 100 местах, изменение требует изменений для всех 100. Но с помощью интерфейса вы можете поместить новое изменение в новый интерфейс и просто использовать этот интерфейс там, где это необходимо (Interface Seq. Из SOLID). Кроме того, использование памяти выглядит так, как если бы интерфейс был меньше, поскольку объект в примере интерфейса используется только один раз в памяти, несмотря на то, сколько мест реализует интерфейс.
источник
Интерфейсы используются для обеспечения согласованности способом, который слабо связан, что отличает его от абстрактного класса, который тесно связан. Вот почему его также обычно определяют как контракт. Какие классы, реализующие интерфейс, должны соблюдать «правила / синтаксис» определяется интерфейсом, и в нем нет конкретных элементов.
Я просто приведу пример, поддерживаемый графикой ниже.
Представьте, что на заводе есть 3 типа машин. Прямоугольная машина, треугольная машина и многоугольная машина. Время конкурентное, и вы хотите упростить обучение операторов. Вы просто хотите обучить их одной методике запуска и остановки машин, чтобы вы могли у вас есть зеленая кнопка запуска и красная кнопка остановки. Так что теперь на 3 разных машинах у вас есть согласованный способ запуска и остановки 3 разных типов машин. Теперь представьте, что эти машины являются классами, и у классов должны быть методы start и stop, как вы будет обеспечивать согласованность между этими классами, которые могут сильно отличаться? Интерфейс - это ответ.
Простой пример, который поможет вам визуализировать, можно спросить, почему бы не использовать абстрактный класс? С интерфейсом объекты не должны быть напрямую связаны или наследоваться, и вы все равно можете обеспечить согласованность между различными классами.
источник
Позвольте мне описать это с другой точки зрения. Давайте создадим историю в соответствии с примером, который я показал выше;
Программа, Машина и IMachine являются актерами нашей истории. Программа хочет работать, но у нее нет этой способности, и машина знает, как ее запустить. Machine и IMachine - лучшие друзья, но Программа не разговаривает с Machine. Таким образом, Program и IMachine заключили сделку и решили, что IMachine расскажет Программе, как работать с помощью машины (как отражатель).
И программа учится бегать с помощью IMachine.
Интерфейс обеспечивает связь и разработку слабо связанных проектов.
PS: у меня есть метод конкретного класса как частный. Моя цель здесь заключается в том, чтобы добиться слабой связи, не допуская доступа к свойствам и методам конкретного класса, и оставить только способ доступа к ним через интерфейсы. (Так что я определил методы интерфейсов явно).
источник
Я знаю, что очень поздно .. (почти девять лет), но если кто-то хочет небольшого объяснения, тогда вы можете пойти на это:
Проще говоря, вы используете интерфейс, когда знаете, что может делать объект или какую функцию мы будем реализовывать на объекте. Пример Вставка, обновление и удаление.
Важное примечание: интерфейсы ВСЕГДА общедоступны.
Надеюсь это поможет.
источник