Почему я хочу использовать интерфейсы?

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

20 ответов

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

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

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

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

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

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

Самым простым способом понимания интерфейсов является то, что они позволяют различным объектам предоставлять ОБЩУЮ функциональность. Это позволяет программисту писать гораздо более простой и короткий код, который программирует интерфейс, тогда, пока объекты реализуют этот интерфейс, он будет работать.

Пример 1: Существует много разных поставщиков баз данных, MySQL, MSSQL, Oracle и т. Д. Однако все объекты базы данных могут ДЕЛАТЬ одни и те же вещи, поэтому вы найдете множество интерфейсов для объектов базы данных. Если объект реализует IDBConnection, он предоставляет методы Open() и Close(). Поэтому, если я хочу, чтобы моя программа не зависела от поставщика базы данных, я программирую на интерфейс, а не на конкретных поставщиков.

IDbConnection connection = GetDatabaseConnectionFromConfig()
connection.Open()
// do stuff
connection.Close()

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

Пример 2: Если вы заметили, что почти все коллекции реализуют этот интерфейс, называемый IEnumerable. IEnumerable возвращает IEnumerator, который имеет MoveNext(), Current и Reset(). Это позволяет C# легко перемещаться по вашей коллекции. Причина, по которой он может это сделать, заключается в том, что он предоставляет интерфейс IEnumerable, который ЗНАЕТ, что объект предоставляет методы, необходимые для его прохождения. Это делает две вещи. 1) циклы foreach теперь будут знать, как перечислять коллекцию, и 2) теперь вы можете применять мощные выражения LINQ к вашей коллекции. Опять же, причина, по которой интерфейсы так полезны, заключается в том, что все коллекции имеют что-то общее, их можно перемещать. Каждую коллекцию можно перемещать по-разному (связанный список или массив), но прелесть интерфейсов в том, что реализация скрыта и не имеет отношения к потребителю интерфейса. MoveNext() дает вам следующий элемент в коллекции, не имеет значения, КАК он это делает. Довольно мило, а?

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

И, конечно, я должен привести мой любимый полиморфный пример C++, пример животных. Все животные имеют определенные характеристики. Допустим, они могут двигаться, говорить, и у всех них есть имя. Так как я только определил, что общего у всех моих животных, я могу абстрагировать эти качества в интерфейс IAnimal. Затем я создаю объект Bear, объект Owl и объект Snake, которые реализуют этот интерфейс. Причина, по которой вы можете хранить разные объекты вместе, реализующие один и тот же интерфейс, заключается в том, что интерфейсы представляют взаимосвязь IS-A. Медведь-это животное, сова-это животное, так что с тех пор я могу собрать их всех как животных.

var animals = new IAnimal[] = {new Bear(), new Owl(), new Snake()} // here I can collect different objects in a single collection because they inherit from the same interface

foreach (IAnimal animal in animals) 
{
    Console.WriteLine(animal.Name)
    animal.Speak() // a bear growls, a owl hoots, and a snake hisses
    animal.Move() // bear runs, owl flys, snake slithers
}

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

Итак, опять же, самое важное в интерфейсах - это то, что общего у объектов, так что вы можете программировать против РАЗНЫХ объектов ТО ЖЕ ЖЕ. Экономит время, создает более гибкие приложения, скрывает сложность / реализацию, моделирует реальные объекты / ситуации и многие другие преимущества.

Надеюсь это поможет.

Интерфейсы определяют контракты, и это ключевое слово.

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

Итак, давайте посмотрим на пример. Предположим, у вас есть метод, который обеспечивает функциональность для сортировки списка. Первым делом.. что за список? Вы действительно заботитесь о том, какие элементы он содержит для сортировки списка? Ваш ответ должен быть "нет". В.NET (например) у вас есть интерфейс под названием IList, который определяет операции, которые ДОЛЖЕН поддерживать список, чтобы вам не было дела до фактических деталей под поверхностью.

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

interface IComparable
{
  // Return -1 if this is less than CompareWith
  // Return 0 if object are equal
  // Return 1 if CompareWith is less than this
  int Compare(object CompareWith);
}

этот контракт указывает, что метод, который принимает объект и возвращает int, должен быть реализован, чтобы быть сопоставимым. Теперь вы определили контракт, и на данный момент вам важен не сам объект, а контракт, поэтому вы можете просто выполнить:

IComparable comp1 = list.GetItem(i) as IComparable;

if (comp1.Compare(list.GetItem(i+1)) < 0)
  swapItem(list,i, i+1)

PS: я знаю, что примеры немного наивные, но они примеры...

Типичным примером является архитектура плагина. Разработчик A пишет основное приложение и хочет убедиться, что все плагины, написанные разработчиками B, C и D, соответствуют тому, что его приложение ожидает от них.

Педали на автомобиле реализуют интерфейс. Я из США, где мы едем по правой стороне дороги. Наши рули находятся на левой стороне автомобиля. Педали для механической коробки передач слева направо - сцепление -> тормоз -> акселератор. Когда я поехал в Ирландию, движение изменилось. Рулевые колеса автомобилей находятся справа, и они едут по левой стороне дороги... но педали, ах педали... они реализовали тот же интерфейс... все три педали были в том же порядке... поэтому, даже если класс был другим, а сеть, в которой работал этот класс, была другой, мне все равно было удобно работать с интерфейсом педали. Мой мозг мог вызывать мои мышцы на этой машине, как и на любой другой машине.

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

When you need different classes to share same methods you use Interfaces.

Интерфейсы абсолютно необходимы в объектно-ориентированной системе, которая рассчитывает на эффективное использование полиморфизма.

Классическим примером может быть IVehicle, у которого есть метод Move(). Вы можете иметь классы Car, Bike и Tank, которые реализуют IVehicle. Все они могут использовать Move (), и вы могли бы написать код, который не заботился о том, с каким транспортным средством он имел дело, просто чтобы он мог использовать Move().

void MoveAVehicle(IVehicle vehicle)
{
    vehicle.Move();
}

Интерфейсы являются формой полиморфизма. Пример:

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

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

Файловые дескрипторы C - это в основном интерфейс "что-то, из чего я могу читать и / или записывать байты и / или в", и почти каждый типизированный язык имеет такие интерфейсы, скрывающиеся в его стандартных библиотеках: потоки или что-то еще. Нетипизированные языки обычно имеют неформальные типы (возможно, называемые контрактами), которые представляют потоки. Таким образом, на практике вам практически никогда не нужно изобретать этот конкретный интерфейс самостоятельно: вы используете то, что дает вам язык.

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

Представьте себе следующий базовый интерфейс, который определяет базовый механизм CRUD:

interface Storable {
    function create($data);
    function read($id);
    function update($data, $id);
    function delete($id);
}

Из этого интерфейса вы можете сказать, что любой объект, который его реализует, должен иметь функциональность для создания, чтения, обновления и удаления данных. Это может быть связано с подключением к базе данных, устройством чтения файлов CSV и устройством чтения файлов XML или любым другим механизмом, который может захотеть использовать операции CRUD.

Таким образом, теперь вы можете получить что-то вроде следующего:

class Logger {
    Storable storage;

    function Logger(Storable storage) {
        this.storage = storage;
    }

    function writeLogEntry() {
        this.storage.create("I am a log entry");
    }
}

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

Тогда возникает следующий вопрос: если базы данных, CSV-файлы и т. Д. Могут хранить данные, не должны ли они быть унаследованы от общего объекта Storable и, таким образом, избавиться от необходимости в интерфейсах? Ответ на этот вопрос - нет... не каждое соединение с базой данных может реализовывать операции CRUD, и то же самое относится ко всем программам чтения файлов.

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

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

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

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

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

Лучший Java-код, который я когда-либо видел, определял почти все ссылки на объекты как экземпляры интерфейсов, а не экземпляры классов. Это сильный знак качества кода, разработанного для гибкости и изменений.

В статье в моем блоге я кратко опишу три цели интерфейсов.

Интерфейсы могут иметь разные цели:

  • Обеспечить различные реализации для одной и той же цели. Типичным примером является список, который может иметь разные реализации для разных вариантов использования производительности (LinkedList, ArrayList и т. Д.).
  • Разрешить изменение критериев. Например, функция сортировки может принимать интерфейс Comparable для обеспечения любого вида критериев сортировки на основе того же алгоритма.
  • Скрыть детали реализации. Это также облегчает пользователю чтение комментариев, поскольку в теле интерфейса есть только методы, поля и комментарии, и нет длинных кусков кода, которые можно пропустить.

Вот полный текст статьи: http://weblogs.manas.com.ar/ary/2007/11/

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

Например:

public interface IExample
{
    void Foo();
}

public class Example : IExample
{
    // explicit implementation syntax
    void IExample.Foo() { ... }
}

/* Usage */
Example e = new Example();

e.Foo(); // error, Foo does not exist

((IExample)e).Foo(); // success

Я думаю, что вам нужно хорошо понять шаблоны проектирования, чтобы увидеть их силу.

Проверьте Head First Design Patterns

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

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

Во-первых, они дают вам дополнительный уровень абстракции. Вы можете сказать "Для этой функции этот параметр должен быть объектом, который имеет эти методы с этими параметрами". И вы, вероятно, хотите также установить значение этих методов в несколько абстрагированных терминах, но в то же время позволить вам рассуждать о коде. На языках с уткой вы получаете это бесплатно. Нет необходимости в явном синтаксисе "интерфейсы". Тем не менее, вы, вероятно, все еще создаете набор концептуальных интерфейсов, что-то вроде контрактов (как в Design by Contract).

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

В целом, они уменьшают сцепление в вашем коде. Это хорошая вещь.

Ваш код также может быть проще для тестирования таким образом.

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

Затем вы можете указать интерфейс ICollection с помощью методов add(), remove() и contains().

Код, который не должен знать, какой тип коллекции (List, Array, Hash-table, Red-black tree и т. Д.) Может принимать объекты, которые реализовали интерфейс, и работать с ними, не зная их фактического типа.

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

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

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

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

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

Здесь есть еще несколько ответов на аналогичный вопрос: интерфейс против базового класса

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