В интернете много статей на тему интерфейсов, что это такое и как их реализовывать. Но я не нашел внятного ответа кто и зачем их придумал? Я только начинаю изучать C# и вообще не вижу смысла в интерфейсах. Классы удобны например в ситуации когда нам нужно в игре создать много врагов, нам не надо каждому врагу прописовать параметры, можно все задать в классе. Вот пример с метанина:
interface IMovable
{
void Move();
}
Реализация в классе:
// применение интерфейса в классе
class Person : IMovable
{
public void Move()
{
Console.WriteLine("Человек идет");
}
}
// применение интерфейса в структуре
struct Car : IMovable
{
public void Move()
{
Console.WriteLine("Машина едет");
}
}
Но если я просто удалю интерфейс и сделаю вот так
class Person
{
public void Move()
{
Console.WriteLine("Человек идет");
}
}
Код по прежнему будет работать.
Вот и вопрос зачем все это нужно?
Я просто не могу представить себе ситуацию где бы было необходимо использовать интерфейсы.
В C#8 добавили реализацию метода по умолчанию в интерфейсе тем самым сделав из интерфейса недо-класс.
Смотрю видео на ютубе и там все используют интерфейсы но не говорят зачем, просто как будто так и надо.
Не спроста же все их используют? Но без понимания зачем все это нужно у меня просто не получается найти смысл их применения и понять как работает чужой код когда в нем есть интерфейсы.
Единственный смысл в них вижу в том что не нужно писать документацию к классам, просто наследуем от интерфейса а дальше пусть уже другой программист лезет в код и смотрит чего там и как.
Ни в коем случае не хочу сказать что я тут самый умный а все остальные ошибались, просто хочу разобраться почему придумали интерфейсы если и без них все хорошо работает?
Извините что так сумбурно написал, сам на эмоциях, неделю уже читаю статьи по теме и ни как не могу осилить.
Есть как минимум 4 причины для использования интерфейсов. Их может и больше, но я текст пишу из головы, потому остальные причины, если такие есть, загуглите или додумаете сами.
Давайте начнем с того, как строятся большие и сложные системы. Основной механизм построения чего то большого - это разделение этого большого на малые части, определения того, как эти части взаимодействуют, и после этого программирование каждой из частей.
Ключевой момент здесь в том, что вы не можете сразу продумать все эти мелкие части и сазу написать все классы для них.
Давайте возьмем типичный пример. Вы пишете свою игру и вам надо предусмотреть сохранение вашей игры. Вы точно знаете, что именно вам надо сохранить, но вы ещё не знаете, куда вы будете сохранять, в файл или в БД или передавать данные на сервер. Что делать в такой ситуации?
В такой ситуации вам поможет интерфейс. Например, у вас есть класс, который вам надо сохранить.
public class MyGameState
{
//... my game data
}
Напишем для него интерфейс сохранения
public interface IGameStore
{
void SaveGame(MyGameState state);
MyGameState Load();
}
Что тут произошло? Вашего сохранения ещё в природе нет, кода для него нет, какой это будет класс, какая у класса-хранилища будет иерария наследования, ничего не известно, но вы уже определили, как ваша игра будет взаимодействовать с этим, ещё не существующим классом. Вы можете этот класс написать позже, или его может написать любой другой программист и все будет работать, так как контракт вашего будушего класса уже известен - вы уже определили с помощью интерфейса все нужные для класса методы.
Другими словами, интерфейс помогает определять взаимодействие между модулями вашей программы, даже если эти модули ещё не существуют.
Но тут вы можете возразить мне и сказать - что вы с таким же успехом можете определить класс для сохранения, например
public absract class GameStore
{
public abstract void SaveGame(MyGameState state);
public abstract MyGameState Load();
}
Да, это верно, вы можете определить абстрактный класс. В таком случае, для реализации сохранения, вам надо будет унаследоваться от абстрактрого класса и реализовать нужные методы. Но давайте подумаем, какие ограничения это накладывает на наш класс сохранения? А вот какие - наш класс сохранения должен будет быть унаследован именно от указанного абстрактного класса и больше ни от кого. Вы можете возразить - мол, в случае интерфейса будет то же самое - ведь класс сохранения должен его реализовать. Но вот и нет - в C# реализация интерфейса никак не ограничивает класс, так как класс может реализовать сколько угодно интерфейсов. А вот наследование от абстрактного класса - уже дело другое, ведь вы не можете насловаться от 2 классов. Отсюда вторая причина использования интерфйсов - Использование интерфйсов позволяет обойти ограничения множественного наследования.
Окей, ну, допустим, мы решили, что мы будем сохранять нашу игру, но для сохранения нашей игры, нам обязательно надо знать имя текушего игрока - то есть его никнейм. Это требование леко выразить, добавив в наш абстрактный класс конструктор, который принимает имя игрока, например (код может не копилироваться, я его практически на телефоне пишу, но идея должна быть ясна)
public absract class GameStore
{
protected string PlayerName{get; protected set;}
public GameStore(string playerName)
{
PlayerName = playerName;
}
public abstract void SaveGame(MyGameState state);
public abstract MyGameState Load();
}
Теперь посмотрите что произошло. Мы не только заставляем классы для сохранения игры наследоваться от нашего абстрактного класса, но ещё и накладываем на них определенные огреничения. Интересны ли эти ограничения остальной части игры? Думаю, нет. В таком случае, зачем остальной части игры знать об этих огреничениях? Нет никаких причин для этого. Таким образом, Используя интерфейс, вы декларируете желаемое поведение. Используя класс - вы декларируете детали реализации. То есть, когда у вас был интерфейс в игре, вы декларировали, что "не важно что тут будет за объект, не важно как он будет написан, но мне надо чтобы он мог сохранять и загружать состояние игры" - то есть вам было важно, что объект может делать, при этом не важно, как он это делает. В этом суть инкапсуляции. Когда же вы используете класс, особенно если класс содержит какие либо посторонние детали, вы уже покажываете, что вам важно не только что объект может делать, но и также кем объект является и какие у объекта есть детали реализации.
А теперь давайте посмотрим на это с точки зрения игры. Игре, на самом деле, не интересно знать, как и куда будет происходить сохранение. Игре не интересны детали сохранения. Игре не интересен даже факт - сущестует ли уже написанный класс сохранения. Все, что игре надо - это получить объект, который содержит нужные игре методы. Всё. При таком подходе связь между игрой и реальным классом сохранения лежит только через интерфейс, что является связью гораздо более слабой. Сам класс может быть написан как угодно, содержать какие угодно огранчения или детали реализации, эти ограничения и детали могут меняться и быть переписаны любое количество раз и все будет работать, пока класс сохранения реализует интерфейс. Таким образом, связь классов через интерфейс является более слабой связью, чем связь классов через абстрактный класс или напрямую, откуда следует выводы:
Классы выражают основную иерархию и часто определяют внутреннее поведение наследников.
Интерфейсы выражают отдельные внешние черты поведения и относительно небольшие иерархии. Либо такие черты поведения, которые присущи разным иерархиям классов, и важны для какого-то кода, притом на основное поведение ему всё равно.
Например, может быть большая иерархия живых организмов – царства, семейства, виды из биологии. Это всё много разных абстрактных и финальных классов. При этом, можно выделить дополнительные иерархии – например, способность передвигаться (IMovable
), которая может наследоваться в способность ходить (IWalking
), летать (IFlying
), плавать (ISwimming
). Что немаловажно, те же интерфейсы могут быть реализованы классами из совсем другой иерархии вроде техники – автомобили, танки, самолёты, корабли и лодки.
Интерфейсы выражают что-то важное для поведения, но не связанное с основной реализацией. Да, они могут иметь свои методы, но это связано опять же с конкретной темой интерфейса, и эти методы могут полагаться лишь на методы интерфейса, просто расширяя их.
Например, ISwimming
может предоставлять абстрактный метод void swim(float speed);
и конкретные методы вроде void swimSlowly() { self.swim(0.3); }
.
Пример не реалистичный, но на деле всё то же самое, только вместо всем понятных животных и техники чаще абстрактные структуры данных и хитроумные манипуляции с ними. Из наиболее популярных это возможности сериализации (ISerializable
), отображения с форматированием, конвертирования в какие-то форматы и т.п. И могут быть большие функции или даже библиотеки, которые будут принимать объекты таких интерфейсов для передачи по сети, сохранения в файл или красивого отображения. А до основного поведения класса, будь то бинарное дерево, граф вычислений, картинка или персонаж игры, им дела нет.
Почему придумали интерфейсы если и без них все хорошо работает?
Конечно, в одной монолитной кодовой базе можно реализовать всё в одной иерархии классов, но в крупном проекте с кучей функционала эти классы получатся очень перегруженными. А промышленная разработка куда сложнее, в ней много инкапсуляции, библиотек и фреймворков, которые разрабатываются разными командами. И там без интерфейсов, которые позволяют связывать разные уровни абстракции и код разных команд без ограничения в реализации, было бы сложно.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Вот разметка ComboBox:
Сравниваю 1 и 2 текстовый файл, если в 1 нету тех строк, которые во 2 текстовом файле, то он создает третий текстовый файл и записывает в негоКак...
У меня на сайте есть галерея картинокКартинки в довольно хорошем качестве, где-то 2000х1500
Есть сайт, когда ссылку на страницу сайта кидаю, например, в skype, то с сайта подтягивается картинка, тайтл и описание, которые находятся в мета-тегах...