Outros explicaram muito bem o problema com os singletons em geral. Gostaria apenas de acrescentar uma observação sobre o caso específico do Logger. Concordo com você que geralmente não é um problema acessar um Logger (ou o logger root, para ser mais preciso) como um singleton, por meio de um método estático getInstance()
ou getRootLogger()
. (a menos que você queira ver o que é registrado pela classe que você está testando - mas, em minha experiência, dificilmente me lembro desses casos em que isso foi necessário. Novamente, para outros, essa pode ser uma preocupação mais urgente).
IMO geralmente um registrador de singleton não é uma preocupação, pois não contém nenhum estado relevante para a classe que você está testando. Ou seja, o estado do logger (e suas possíveis mudanças) não têm nenhum efeito sobre o estado da classe testada. Portanto, isso não torna seus testes de unidade mais difíceis.
A alternativa seria injetar o logger por meio do construtor em (quase) todas as classes em seu aplicativo. Para consistência de interfaces, deve ser injetado mesmo se a classe em questão não registrar nada no momento - a alternativa seria que quando você descobrir em algum ponto que agora você precisa registrar algo desta classe, você precisa de um logger, portanto você precisa adicionar um parâmetro de construtor para DI, quebrando todo o código do cliente. Não gosto de ambas as opções e sinto que usar DI para registro seria apenas complicar minha vida para cumprir uma regra teórica, sem nenhum benefício concreto.
Portanto, meu ponto principal é: uma classe que é usada (quase) universalmente, mas não contém o estado relevante para seu aplicativo, pode ser implementada com segurança como Singleton .