A herança de classe e as interfaces têm seu lugar. Herança significa "é um", enquanto uma interface fornece um contrato que define como algo "se comporta".
Eu diria que usar interfaces com mais frequência não é uma prática ruim. Atualmente, estou lendo "C # eficaz - 50 maneiras específicas de melhorar seu c #", de Bill Wagner. O item número 22 declara e cito "Prefiro definir e implementar interfaces à herança".
Geralmente, uso classes base quando preciso definir uma implementação específica de comportamento comum entre tipos conceitualmente relacionados. Mais frequentemente eu uso interfaces. Na verdade, normalmente começo definindo uma interface para uma classe quando começo a criar uma ... mesmo que no final não compile a interface, acho que ajuda começar definindo a API pública do classe desde o início. Se eu achar que tenho várias classes implementando a interface e a lógica de implementação é idêntica, só então me perguntarei se faria sentido ou não implementar uma classe base comum entre os tipos.
Algumas citações do livro de Bill Wagners ...
todas as classes derivadas incorporam imediatamente esse comportamento. Adicionar um membro a uma interface quebra todas as classes que implementam essa interface. Eles não conterão o novo método e não serão mais compilados. Cada implementador deve atualizar esse tipo para incluir o novo membro. Escolher entre uma classe base abstrata e uma interface é uma questão de como apoiar melhor suas abstrações ao longo do tempo. As interfaces são corrigidas: você libera uma interface como um contrato para um conjunto de funcionalidades que qualquer tipo pode implementar. As classes base podem ser estendidas ao longo do tempo. Essas extensões se tornam parte de todas as classes derivadas. Os dois modelos podem ser combinados para reutilizar o código de implementação enquanto suportam várias interfaces ". Eles não conterão o novo método e não serão mais compilados. Cada implementador deve atualizar esse tipo para incluir o novo membro. Escolher entre uma classe base abstrata e uma interface é uma questão de como apoiar melhor suas abstrações ao longo do tempo. As interfaces são corrigidas: você libera uma interface como um contrato para um conjunto de funcionalidades que qualquer tipo pode implementar. As classes base podem ser estendidas ao longo do tempo. Essas extensões se tornam parte de todas as classes derivadas. Os dois modelos podem ser combinados para reutilizar o código de implementação enquanto suportam várias interfaces ". Eles não conterão o novo método e não serão mais compilados. Cada implementador deve atualizar esse tipo para incluir o novo membro. Escolher entre uma classe base abstrata e uma interface é uma questão de como apoiar melhor suas abstrações ao longo do tempo. As interfaces são corrigidas: você libera uma interface como um contrato para um conjunto de funcionalidades que qualquer tipo pode implementar. As classes base podem ser estendidas ao longo do tempo. Essas extensões se tornam parte de todas as classes derivadas. Os dois modelos podem ser combinados para reutilizar o código de implementação enquanto suportam várias interfaces ". Você libera uma interface como um contrato para um conjunto de funcionalidades que qualquer tipo pode implementar. As classes base podem ser estendidas ao longo do tempo. Essas extensões se tornam parte de todas as classes derivadas. Os dois modelos podem ser combinados para reutilizar o código de implementação enquanto suportam várias interfaces ". Você libera uma interface como um contrato para um conjunto de funcionalidades que qualquer tipo pode implementar. As classes base podem ser estendidas ao longo do tempo. Essas extensões se tornam parte de todas as classes derivadas. Os dois modelos podem ser combinados para reutilizar o código de implementação enquanto suportam várias interfaces ".
"As interfaces de codificação fornecem maior flexibilidade a outros desenvolvedores do que a codificação para tipos de classe base".
"O uso de interfaces para definir APIs para uma classe fornece maior flexibilidade."
"Quando seu tipo expõe propriedades como tipos de classe, expõe toda a interface a essa classe. Usando interfaces, você pode optar por expor apenas os métodos e propriedades que deseja que os clientes usem."
"As classes base descrevem e implementam comportamentos comuns entre tipos de concreto relacionados. As interfaces descrevem peças atômicas de funcionalidade que tipos de concreto não relacionados podem implementar. Ambas têm seu lugar. As classes definem os tipos que você cria. As interfaces descrevem o comportamento desses tipos como peças de funcionalidade. Se você entender as diferenças, criará designs mais expressivos e mais resilientes diante da mudança. Use hierarquias de classes para definir tipos relacionados. Exponha a funcionalidade usando interfaces implementadas nesses tipos ".