Tanto la herencia de clase como las interfaces tienen su lugar. Herencia significa "es un" mientras que una interfaz proporciona un contrato que define cómo se comporta algo ".
Yo diría que usar interfaces con más frecuencia no es una mala práctica. Actualmente estoy leyendo "C # efectivo - 50 formas específicas para mejorar tu C #" por Bill Wagner. El artículo número 22 dice, y cito, "Prefiero definir e implementar interfaces a la herencia".
Generalmente, uso clases básicas cuando necesito definir una implementación específica de comportamiento común entre tipos relacionados conceptualmente. Más a menudo utilizo interfaces. De hecho, normalmente comienzo por definir una interfaz para una clase cuando empiezo a crear una ... incluso si al final no compilo la interfaz, creo que es útil comenzar definiendo la API pública de clase desde el principio Si encuentro que tengo varias clases implementando la interfaz, y la lógica de implementación es idéntica, solo entonces me preguntaré si tendría sentido o no implementar una clase base común entre los tipos.
Un par de citas del libro de Bill Wagners ...
"Las clases base abstractas pueden proporcionar alguna implementación para tipos derivados, además de describir el comportamiento común. Puede especificar miembros de datos, métodos concretos, implementación para métodos virtuales, propiedades, eventos e indizadores. Una clase base puede proporcionar implementación Para algunos de los métodos, se proporciona una reutilización de implementación común. Cualquiera de los elementos puede ser virtual, abstracto o no virtual. Una clase base abstracta puede proporcionar una implementación para cualquier comportamiento concreto; las interfaces no. Esta reutilización de implementación brinda otro beneficio: agregue un método a la clase base, todas las clases derivadas se mejoran automática e implícitamente. En ese sentido, las clases base brindan una manera de extender el comportamiento de varios tipos de manera eficiente a lo largo del tiempo: al agregar e implementar la funcionalidad en la clase base, todas las clases derivadas incorpore inmediatamente ese comportamiento. Agregar un miembro a una interfaz rompe todas las clases que implementan esa interfaz. Mantener el nuevo método y ya no compilará. Cada implementador debe actualizar ese tipo para incluir al nuevo miembro. La elección entre una clase base abstracta y una interfaz es una cuestión de cómo apoyar mejor sus abstracciones a lo largo del tiempo. Las interfaces son fijas: libera una interfaz como un contrato para un conjunto de funciones que cualquier tipo puede implementar. Las clases base pueden extenderse en el tiempo. Esas extensiones se convierten en parte de cada clase derivada. Los dos modelos se pueden mezclar para reutilizar el código de implementación al mismo tiempo que admiten múltiples interfaces ".
"Las interfaces de codificación ofrecen mayor flexibilidad a otros desarrolladores que la codificación a tipos de clase base".
"El uso de interfaces para definir API para una clase proporciona una mayor flexibilidad".
"Cuando su tipo expone las propiedades como tipos de clase, expone la interfaz completa a esa clase. Usando interfaces, puede elegir exponer solo los métodos y propiedades que desea que usen los clientes."
"Las clases base describen e implementan comportamientos comunes en los tipos concretos relacionados. Las interfaces describen piezas atómicas de funcionalidad que pueden implementar los tipos concretos no relacionados. Ambos tienen su lugar. Las clases definen los tipos que creas. Las interfaces describen el comportamiento de esos tipos como piezas de funcionalidad. Si comprende las diferencias, creará diseños más expresivos que sean más resistentes ante el cambio. Use jerarquías de clase para definir tipos relacionados. Expóngalo mediante interfaces implementadas en esos tipos ".