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

Я понимаю что с помощью интерфейса, можно создать отдельный метод к примеру IPrinter в котором есть метод Print() и не важно, как именно печатает принтер (лазерный или струйный). Знаю что класс может реализовывать один и тот же интерфейс по-своему, это позволяет работать с объектами через общий тип. Это все что я понимаю,но разве в этом толк интерфейса ? Может с помощью интерфейса можно что-то ещё крутое сделать? Можете пожалуйста объяснить и если можно на вот таком примере: Есть игра в которой есть юниты (танки, машины, фуры, корабли и т.д) так же разные местности (горы, океаны, зимные биомы) и вот как тут можно применить интерфейсы ? Типа создать к примеру интерфейс движения и применить его к разным юнитам с разными применениями движения?


Ответы (4 шт):

Автор решения: InterceptorTSK

Есть родитель, есть потомок - это логическое отношение. Здесь нужно рассматривать строго типы. Не важно какие у них методы, дело не в этом.

Суть всегда в информации, которая всегда суть - абстракция.

Есть Информация А, есть информация Б.

Пусть Информация А чуть больше чем информация Б, и вся информация Б включается в Информацию А. В таком случае сразу возможно отношение Родитель/Потомок.

С интерфейсами ровно такая же история. Интерфейс - суть родитель чего то, какой то абстракции.

Но тогда встаёт закономерный вопрос. Существуют в языке родители-типы - как классы, и родители-типы - как интерфейсы. Куда где чего применять? На чём строить ООП, как набор типизации выстраиваемой информации в иерархию типов?

Любые типы должны строиться на реальности, во всяком случае к этому стремятся. Тут помогают примеры, которые вы просите, которые вы привели. На самом деле всё это применимо и к базам данных, потому что БД тоже иерархичны, и данные ровно так же имеют иерархии, т.е. типизацию.

На чём всё строится? На чём строится логика типизации? Это логическая система, называется формальной системой, или формализмом. Т.е. выстраиваемая логика типов - это формализм.

Что преследует любой формализм? Это такая логическая система, которая не противоречит в рамках самоё себя. Иначе - формализм/логика плохие, противоречивая логика получается. Это требует прямой проверки. Это всё [выстраиваемая логика, т.е. формализм] проверяется на утверждения сразу на момент построения этой логики.

Простой пример, классификация живности Карла Линнея. Классификация как раз и есть типичный пример вменяемой типизации, на которой можно построить типизацию ООП.

Есть Котик как абстрактный тип, он же - информация. Есть Пёсик как абстрактный тип, он же - информация.

Но есть общая информация от Котика и Пёсика. Это ближайшее общее - суть ближайший родительский тип. Назовём этот тип - Животное.

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

   Животное
    /    \
Котик    Пёсик

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

Котик - это Котик
Пёсик - это Пёсик
Котик - это Животное
Пёсик - это Животное
Котик - (Животное)Котик
Пёсик - (Животное)Пёсик
Животное - это Животное

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

Вкрутим хвост. Хвост - это информация. Котик, Пёсик, Животное - не могут быть приводимы к Хвосту как к типу, Хвост не может быть приводим к Котику, Пёсику, Животному как к типам. Никак.

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

   Животное
      |
   Хордовое - IХвостатое
    /    \
Котик    Пёсик

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

Котик - это Котик
Пёсик - это Пёсик
Котик - это Хордовое
Пёсик - это Хордовое
Котик - (Хордовое)Котик
Пёсик - (Хордовое)Пёсик
Котик - (IХвостатое)Котик
Пёсик - (IХвостатое)Пёсик
Хордовое - это IХвостатое
Хордовое - это Хордовое
IХвостатое - это (IХвостатое)Хордовое
IХвостатое - это IХвостатое
Хордовое - это Животное
Животное - это Животное

Заметьте следующее, здесь есть суть.

Хордовое, Котик, Пёсик - приводимы к IХвостатое. Это то что нужно. Это вопрос об интерфейсе, зачем он нужен. Иначе говоря это родительский тип для Хордового, Котика, Пёсика. Именно так, интерфейс - это родительский тип.

Но это частичный родительский тип для Хордового. Это называют "половиной" настоящего родительского типа Хордового. Или это "треть-родительский" тип. Разумеется, это условность.

Интерфейс - это тип ограниченного функционала. Навешивается на "настоящий" тип, и ограничивает функционал "настоящего" типа. С точки зрения типизации.

Теперь совсем конкретно.

Котик - это Хордовое, и оно же IХвостатое
Пёсик - это Хордовое, и оно же IХвостатое
Хордовое - это Хордовое, и оно же IХвостатое

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

Это существует строго в рамках теории информации, там рассматривается, как теория и практика. На этом существует типизация как таковая, потому что типизация с пустого места не берётся, с неба не падает. Основа типизации - есть теория абстракций как таковая, как формальная система, как логика.

Программистский интерфейс строится строго на этом же. Изначально строится на типизации. Выделяется некоторая нужная часть типа. К этому частичному типу (типу ограниченного функционала) - может быть приводим сам тип и его потомки.

Итого:

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

ПС: Весьма важно здесь то, что верный граф абстракций всегда единственный. Если вы возьмёте сущности Котика, Пёсика, Хордового, IХвостатого, Животного, и перестроите граф - вы всегда получите противоречивый граф.

→ Ссылка
Автор решения: DedMoroz

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

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

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

→ Ссылка
Автор решения: mazik7512

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

Если рассматривать это на ваших примерах:

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

Допустим у нас будет интерфейс ICanFire с методом Fire(Target target), очевидно AK-74, какой-нибудь линкор и танк Т-90 стреляют сильно по-разному. Например, у линкора может быть несколько орудий и следовательно он может за одно выполнение метода Fire выстрелить несколько раз (из разных орудий), а тот же AK-74 очевидно не может выпустить несколько патронов сразу (допустим, что функция Fire будет обрабатывать выпуск именно одного снаряда/патрона из всех доступных орудий).

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

Или например, рассмотрим пример с местностями:

Интерфейс ITerrain с методом Effect(Unit unit) допустим снега будут давать -70% скорости передвижения танкам и/или +20% скрытности пехотинцам и т.д.

→ Ссылка
Автор решения: Андрей Туманов

Основное отличие наследования Абстрактного класса от реализации Интерфейса состоит в том, что Абстрактный класс делегирует реализацию своих методов ТОЛЬКО СВОИМ потомкам, а Интерфейс делегирует реализацию своих методов ЛЮБЫМ классам.


Поэтому Интерфейс может быть использован и чаще всего используется для декларации возможности использования какой-либо ВНЕШНЕЙ функциональности по отношению к реализуемым конкретным возможностям объектов. Если необходимо использовать РАЗНЫЕ структуры классов одинаково - появляются интерфейсы.

Простейший пример:

Нам нужно отображать на экране все видимые объекты.

При этом в системе есть несколько базовых абстрактных классов, которые представляют разные объекты Оружие, Юниты, Предметы интерфейса итд.

Наследовать все эти классы от одного базового класса по каким-либо причинам не целесообразно.

Поэтому, мы декларируем, что все эти классы РЕАЛИЗУЮТ нужный нам интерфейс отображения на экране. Каждый по своему. Независимо друг от друга.

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


Вторая возможность, которую предоставляет Интерфейс - это ОГРАНИЧЕНИЕ доступной функциональности, предоставляемой каким-либо Абстрактным классом.

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

→ Ссылка