Inni bardzo dobrze wyjaśnili ogólny problem z singletonami. Chciałbym tylko dodać uwagę na temat konkretnego przypadku Loggera. Zgadzam się z Tobą, że dostęp do Loggera (lub dokładniej mówiąc do roota) jako singletona zwykle nie stanowi problemu, za pomocą metody statycznej getInstance()
lub getRootLogger()
metody. (chyba, że chcesz zobaczyć, co jest rejestrowane przez klasę, którą testujesz - ale z mojego doświadczenia nie mogę sobie przypomnieć takich przypadków, w których było to konieczne. Z drugiej strony, dla innych może to być bardziej naglący problem).
IMO zwykle pojedynczy rejestrator nie jest zmartwieniem, ponieważ nie zawiera żadnego stanu związanego z testowaną klasą. Oznacza to, że stan loggera (i jego możliwe zmiany) nie mają żadnego wpływu na stan testowanej klasy. Więc to nie utrudnia testów jednostkowych.
Alternatywą byłoby wstrzyknięcie rejestratora za pośrednictwem konstruktora do (prawie) każdej klasy w aplikacji. Dla spójności interfejsów należy go wstrzyknąć nawet, jeśli dana klasa obecnie niczego nie rejestruje - alternatywą byłoby to, że gdy odkryjesz w pewnym momencie, że teraz musisz coś zalogować z tej klasy, potrzebujesz loggera, a więc musisz dodać parametr konstruktora dla DI, łamiąc cały kod klienta. Nie podoba mi się obie te opcje i czuję, że używanie DI do logowania tylko skomplikowałoby moje życie, aby zachować zgodność z teoretyczną zasadą, bez żadnych konkretnych korzyści.
Więc moim zdaniem: klasa, która jest używana (prawie) powszechnie, ale nie zawiera stanu związanego z twoją aplikacją, może być bezpiecznie zaimplementowana jako Singleton .