И наследование классов, и интерфейсы имеют свое место. Наследование означает «является», в то время как интерфейс предоставляет контракт, который определяет, что «ведет себя как».
Я бы сказал, что использование интерфейсов чаще всего не является плохой практикой. В настоящее время я читаю «Эффективные C # - 50 конкретных способов улучшить ваш C #» Билла Вагнера. Пункт номер 22 гласит, и я цитирую, «Предпочитаю определение и реализацию интерфейсов наследования».
Обычно я использую базовые классы, когда мне нужно определить конкретную реализацию общего поведения между концептуально связанными типами. Чаще всего я использую интерфейсы. Фактически, я обычно начинаю с определения интерфейса для класса, когда начинаю его создавать ... даже если в конце концов я не скомпилирую интерфейс, я обнаружу, что он помогает начать с определения общедоступного API интерфейса. класс с самого начала. Если я обнаружу, что у меня есть несколько классов, реализующих интерфейс, и логика реализации идентична, только тогда я задам себе вопрос, имеет ли смысл реализовывать общий базовый класс между типами.
Пару цитат из книги Билла Вагнера ...
все производные классы немедленно включают это поведение. Добавление члена к интерфейсу нарушает все классы, которые реализуют этот интерфейс. Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Вы освобождаете интерфейс как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Вы освобождаете интерфейс как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ".
«Интерфейсы кодирования обеспечивают большую гибкость для других разработчиков, чем кодирование для базовых типов классов».
«Использование интерфейсов для определения API для класса обеспечивает большую гибкость».
«Когда ваш тип предоставляет свойства как типы классов, он предоставляет весь интерфейс этому классу. Используя интерфейсы, вы можете выбрать отображение только тех методов и свойств, которые вы хотите использовать для клиентов».
«Базовые классы описывают и реализуют общие поведения для связанных конкретных типов. Интерфейсы описывают элементарные части функциональности, которые могут реализовывать несвязанные конкретные типы. Оба имеют свое место. Классы определяют типы, которые вы создаете. Интерфейсы описывают поведение этих типов как части функциональности. Если вы поймете различия, вы создадите более выразительные проекты, которые будут более устойчивыми перед лицом изменений. Используйте иерархии классов для определения связанных типов. Предоставьте функциональность, используя интерфейсы, реализованные в этих типах ».