In primo luogo, comprendi che una classe puramente astratta è in realtà solo un'interfaccia che non può fare eredità multiple.
Scrivi classe, estrai un'interfaccia, è un'attività cerebrale morta. Tanto che abbiamo un refactoring per questo. Che è un peccato Seguendo questo schema "ogni classe ottiene un'interfaccia" non solo produce disordine, ma manca completamente il punto.
Un'interfaccia non dovrebbe essere pensata semplicemente come una riaffermazione formale di ciò che la classe può fare. Un'interfaccia dovrebbe essere pensata come un contratto imposto dall'uso del codice client che dettaglia le sue esigenze.
Non ho alcun problema a scrivere un'interfaccia che al momento ha solo una classe che la implementa. In realtà non mi interessa se nessuna classe lo implementa ancora. Perché sto pensando a ciò che il mio codice usa ha bisogno. L'interfaccia esprime ciò che richiede il codice usando. Qualunque cosa succeda più tardi può fare ciò che vuole fintanto che soddisfa queste aspettative.
Ora non lo faccio ogni volta che un oggetto ne usa un altro. Lo faccio quando valico un confine. Lo faccio quando non voglio che un oggetto sappia esattamente a quale altro oggetto sta parlando. Qual è l'unico modo in cui il polimorfismo funzionerà. Lo faccio quando mi aspetto che l'oggetto con cui sta parlando il mio codice cliente possa cambiare. Certamente non lo faccio quando quello che sto usando è la classe String. La classe String è bella e stabile e non ho bisogno di guardarmi dal cambiare su di me.
Quando decidi di interagire direttamente con un'implementazione concreta piuttosto che attraverso un'astrazione, prevedi che l'implementazione sia abbastanza stabile da non dover cambiare.
Questo è il modo in cui modifico il principio di inversione delle dipendenze . Non dovresti applicarlo alla cieca in modo fanatico a tutto. Quando aggiungi un'astrazione, stai dicendo che non ti fidi della scelta della classe di implementazione di essere stabile per tutta la durata del progetto.
Tutto presuppone che tu stia cercando di seguire il Open Closed Principle . Questo principio è importante solo quando i costi associati a modifiche dirette al codice stabilito sono significativi. Uno dei motivi principali per cui le persone non sono d'accordo su quanto sia importante il disaccoppiamento degli oggetti è perché non tutti sperimentano gli stessi costi quando fanno cambiamenti diretti. Se ritestare, ricompilare e ridistribuire l'intero codice base è banale per te allora risolvere una necessità di cambiamento con la modifica diretta è probabilmente una semplificazione molto interessante di questo problema.
Semplicemente non c'è una risposta cerebrale a questa domanda. Un'interfaccia o una classe astratta non è qualcosa che dovresti aggiungere ad ogni classe e non puoi semplicemente contare il numero di classi di implementazione e decidere che non è necessario. Si tratta di affrontare il cambiamento. Il che significa che stai anticipando il futuro. Non essere sorpreso se ti sbagli. Mantieni il più semplice possibile senza arroccarti in un angolo.
Quindi, per favore, non scrivere astrazioni solo per aiutarci a leggere il codice. Abbiamo strumenti per questo. Usa le astrazioni per disaccoppiare ciò che ha bisogno di disaccoppiare.