І спадкування класу, і інтерфейси мають своє місце. Спадщина означає "є", тоді як інтерфейс надає контракт, який визначає, як щось "поводиться".
Я б сказав, що частіше використовувати інтерфейси - це зовсім не погана практика. Зараз я читаю "Ефективний C # - 50 конкретних способів поліпшити свій C #" від Білла Вагнера. У пункті 22 зазначається, і я цитую "Віддавайте перевагу визначенню та реалізації інтерфейсів для спадкування".
Як правило, я використовую базові класи, коли мені потрібно визначити конкретну реалізацію загальної поведінки між концептуально пов'язаними типами. Частіше я використовую інтерфейси. Насправді я зазвичай починаю з визначення інтерфейсу для класу, коли я починаю створювати його ... навіть якщо врешті-решт я не компілюю інтерфейс, я вважаю, що це допомагає починати, визначаючи загальнодоступний API клас з іти. Якщо я виявлю, що у мене є кілька класів, що реалізують інтерфейс, і логіка реалізації є ідентичною, тільки тоді я запитаю себе, чи було б сенсом реалізувати загальний базовий клас між типами.
Кілька цитат із книги Білла Вагнерса ...
всі похідні класи негайно включають таку поведінку. Додавання члена до інтерфейсу розбиває всі класи, які реалізують цей інтерфейс. Вони не містять нового методу і більше не збираються. Кожен реалізатор повинен оновити цей тип, щоб включити нового учасника. Вибір між абстрактним базовим класом та інтерфейсом - це питання про те, як найкраще підтримувати свої абстракції з часом. Інтерфейси виправлені: ви випускаєте інтерфейс як контракт для набору функціональних можливостей, які може реалізувати будь-який тип. Базові класи можна з часом розширити. Ці розширення стають частиною кожного похідного класу. Дві моделі можна змішати для повторного використання коду реалізації, підтримуючи декілька інтерфейсів. " Вони не містять нового методу і більше не збираються. Кожен реалізатор повинен оновити цей тип, щоб включити нового учасника. Вибір між абстрактним базовим класом та інтерфейсом - це питання про те, як найкраще підтримувати свої абстракції з часом. Інтерфейси виправлені: ви випускаєте інтерфейс як контракт для набору функціональних можливостей, які може реалізувати будь-який тип. Базові класи можна з часом розширити. Ці розширення стають частиною кожного похідного класу. Дві моделі можна змішати для повторного використання коду реалізації, підтримуючи декілька інтерфейсів. " Вони не містять нового методу і більше не збираються. Кожен реалізатор повинен оновити цей тип, щоб включити нового учасника. Вибір між абстрактним базовим класом та інтерфейсом - це питання про те, як найкраще підтримувати свої абстракції з часом. Інтерфейси виправлені: ви випускаєте інтерфейс як контракт для набору функціональних можливостей, які може реалізувати будь-який тип. Базові класи можна з часом розширити. Ці розширення стають частиною кожного похідного класу. Дві моделі можна змішати для повторного використання коду реалізації, підтримуючи декілька інтерфейсів. " Ви випускаєте інтерфейс як договір набір функціональних можливостей, який може реалізувати будь-який тип. Базові класи можна з часом розширити. Ці розширення стають частиною кожного похідного класу. Дві моделі можна змішати для повторного використання коду реалізації, підтримуючи декілька інтерфейсів. " Ви випускаєте інтерфейс як договір набір функціональних можливостей, який може реалізувати будь-який тип. Базові класи можна з часом розширити. Ці розширення стають частиною кожного похідного класу. Дві моделі можна змішати для повторного використання коду реалізації, підтримуючи декілька інтерфейсів. "
"Інтерфейси кодування забезпечують більшу гнучкість для інших розробників, ніж кодування до типів базового класу."
"Використання інтерфейсів для визначення API для класу забезпечує більшу гнучкість."
"Коли ваш тип виставляє властивості як типи класів, він виставляє до цього класу весь інтерфейс. Використовуючи інтерфейси, ви можете викрити лише ті методи та властивості, якими ви хочете користуватися клієнтами."
"Базові класи описують та реалізують загальну поведінку у відповідних конкретних типах. Інтерфейси описують атомні фрагменти функціональності, які можуть реалізувати непов'язані типи бетону. Обидва мають своє місце. Класи визначають типи, які ви створюєте. Інтерфейси описують поведінку цих типів як фрагменти функціональності. Якщо ви розумієте відмінності, ви створите більш виразні конструкції, більш стійкі до зміни змін. Використовуйте ієрархії класів для визначення відповідних типів. Розкрийте функціональність за допомогою інтерфейсів, реалізованих для цих типів. "