Primeiro, entenda que uma classe puramente abstrata é realmente apenas uma interface que não pode fazer herança múltipla.
Classe de gravação, interface de extração, é uma atividade com morte cerebral. Tanto é assim que temos uma refatoração para isso. O que é uma pena. Após esse padrão "toda classe obtém uma interface", não apenas produz desordem, que perde completamente o objetivo.
Uma interface não deve ser pensada como uma simples reformulação formal do que a classe pode fazer. Uma interface deve ser pensada como um contrato imposto pelo código do cliente usando detalhando suas necessidades.
Não tenho problemas em escrever uma interface que atualmente tenha apenas uma classe implementando-a. Na verdade, eu não me importo se nenhuma aula ainda a implementa. Porque estou pensando no que meu código de uso precisa. A interface expressa o que o código em uso exige. O que vier depois pode fazer o que quiser, desde que satisfaça essas expectativas.
Agora não faço isso toda vez que um objeto usa outro. Eu faço isso ao atravessar uma fronteira. Faço isso quando não quero que um objeto saiba exatamente com qual outro objeto está falando. Qual é a única maneira de o polimorfismo funcionar. Faço isso quando espero que o objeto que meu código de cliente está falando provavelmente mude. Certamente não faço isso quando estou usando a classe String. A classe String é agradável e estável e não sinto necessidade de me proteger contra isso.
Quando você decide interagir diretamente com uma implementação concreta, e não através de uma abstração, está prevendo que a implementação é estável o suficiente para confiar em não mudar.
É assim que tempero o Princípio da Inversão da Dependência . Você não deve aplicar cegamente fanaticamente isso a tudo. Quando você adiciona uma abstração, está realmente dizendo que não confia que a escolha da classe de implementação seja estável ao longo da vida do projeto.
Tudo isso pressupõe que você esteja tentando seguir o Princípio Aberto Fechado . Esse princípio é importante apenas quando os custos associados à realização de alterações diretas no código estabelecido são significativos. Uma das principais razões pelas quais as pessoas discordam sobre a importância da dissociação de objetos é porque nem todos experimentam os mesmos custos ao fazer mudanças diretas. Se retestar, recompilar e redistribuir toda a sua base de código for trivial para você, resolver a necessidade de alteração com modificação direta provavelmente será uma simplificação muito atraente desse problema.
Simplesmente não existe uma resposta de morte cerebral para essa pergunta. Uma interface ou classe abstrata não é algo que você deve adicionar a todas as classes e você não pode apenas contar o número de classes de implementação e decidir que não é necessário. É sobre lidar com mudanças. O que significa que você está antecipando o futuro. Não se surpreenda se você errar. Mantenha o mais simples possível, sem recuar no canto.
Portanto, não escreva abstrações apenas para nos ajudar a ler o código. Temos ferramentas para isso. Use abstrações para dissociar o que precisa ser dissociado.