Altri hanno spiegato molto bene il problema con i singleton in generale. Vorrei solo aggiungere una nota sul caso specifico di Logger. Sono d'accordo con te sul fatto che di solito non è un problema accedere a un logger (o al root logger, per la precisione) come singleton, tramite uno statico getInstance()
o un getRootLogger()
metodo. (a meno che tu non voglia vedere cosa viene registrato dalla classe che stai testando - ma nella mia esperienza difficilmente riesco a ricordare casi in cui ciò fosse necessario. D'altra parte, per altri questa potrebbe essere una preoccupazione più urgente).
IMO di solito un logger singleton non è un problema, poiché non contiene alcuno stato rilevante per la classe che stai testando. Cioè, lo stato del logger (e le sue possibili modifiche) non hanno alcun effetto sullo stato della classe testata. Quindi non rende i tuoi test unitari più difficili.
L'alternativa sarebbe iniettare il logger tramite il costruttore, in (quasi) ogni singola classe nella tua app. Per la coerenza delle interfacce, dovrebbe essere iniettato anche se la classe in questione non registra nulla al momento - l'alternativa sarebbe che quando scopri a un certo punto che ora devi registrare qualcosa da questa classe, hai bisogno di un logger, quindi è necessario aggiungere un parametro costruttore per DI, interrompendo tutto il codice client. Non mi piacciono entrambe queste opzioni e ritengo che usare DI per la registrazione complicherebbe la mia vita solo per rispettare una regola teorica, senza alcun beneficio concreto.
Quindi la mia conclusione è: una classe che viene utilizzata (quasi) universalmente, ma non contiene uno stato rilevante per la tua app, può essere tranquillamente implementata come Singleton .