Когда использовать интерфейсы или абстрактные классы? Когда использовать оба?
Хотя в некоторых рекомендациях говорится, что вы должны использовать интерфейс, если вы хотите определить контракт для класса, в котором наследование неясно (IDomesticated
) и наследование, когда класс является продолжением другого (Cat : Mammal
, Snake : Reptile
), бывают случаи, когда (по моему мнению) эти рекомендации попадают в серую зону.
Например, скажем, моя реализация былаCat : Pet
. Pet
это абстрактный класс. Должно ли это быть расширено доCat : Mammal, IDomesticated
гдеMammal
абстрактный класс иIDomesticated
такое интерфейс? Или я в конфликте сПОЦЕЛУЙ/YAGNI принципы (хотя я не уверен, будет лиWolf
класс в будущем, который не сможет унаследовать отPet
)?
Отойдя от метафорическогоCat
с иPet
s, скажем, у меня есть несколько классов, которые представляют источники для входящих данных. Им всем нужно как-то реализовать одну и ту же базу. Я мог бы реализовать некоторый общий код в абстрактномSource
класс и наследовать от него. Я также мог бы просто сделатьISource
интерфейс (который кажется мне более «правильным») и повторно реализовать универсальный код в каждом классе (который менее интуитивно понятен). Наконец, я мог бы «съесть торт и съесть его». делая как абстрактный класс, так и интерфейс. Что лучше?
Эти два случая поднимают вопрос об использовании только абстрактного класса, только интерфейса и как абстрактного класса, так и интерфейса. Это все действительные варианты или есть «правила»? когда один должен быть использован поверх другого?
Я хотел бы пояснить это, используя «абстрактный класс и интерфейс»; это включает в себя случай, когда они по существу представляют собой одно и то же (Source
а такжеISource
оба имеют одинаковых членов), но класс добавляет общие функциональные возможности, в то время как интерфейс определяет контракт.
Также стоит отметить, что этот вопрос в основном относится к языкам, которые не поддерживают множественное наследование (например, .NET и Java).