Dependency Injection & Singleton Design pattern
Singletons are like communism: they both sound great on paper, but explode with problems in practice.
The singleton pattern places a disproportionate emphasis on the ease of accessing objects. It completely eschews context by requiring that every consumer use an AppDomain-scoped object, leaving no options for varying implementations. It embeds infrastructure knowledge in your classes (the call to GetInstance()
) while adding exactly zero expressive power. It actually lessens your expressive power, because you can't change the implementation used by one class without changing it for all of them. You simply can't add one-off pieces of functionality.
Also, when class Foo
depends on Logger.GetInstance()
, Foo
is effectively hiding its dependencies from consumers. This means that you can't fully understand Foo
or use it with confidence unless you read its source and uncover the fact that it depends on Logger
. If you don't have the source, that limits how well you can understand and effectively use the code on which you depend.
The singleton pattern, as implemented with static properties/methods, is little more than a hack around implementing an infrastructure. It limits you in myriad ways while offering no discernible benefit over the alternatives. You can use it however you'd like, but as there are viable alternatives which promote better design, it should never be a recommended practice.
If you want to verify what gets logged in a test, you need dependency injection. Furthermore, a logger is rarely a singleton - generally you have a logger per each of your class.
Watch this presentation on Object-oriented design for testability and you'll see why singletons are bad.
The problem with singletons is that they represent a global state which is hard to predict, especially in tests.
Have in mind that an object can be de-facto singleton but still be obtained via dependency-injection, rather than via Singleton.getInstance()
.
I'm just listing some important points made by Misko Hevery in his presentation. After watching it you will gain full perspective on why it is better to have an object define what its dependencies are, but not define a way how to create them.
Others have explained very well the problem with singletons in general. I would just like to add a note about the specific case of Logger. I agree with you that it is usually not a problem to access a Logger (or the root logger, to be precise) as a singleton, via a static getInstance()
or getRootLogger()
method. (unless if you want to see what gets logged by the class you are testing - but in my experience I can hardly recall such cases where this was necessary. Then again, for others this might be a more pressing concern).
IMO usually a singleton logger is not a worry, since it does not contain any state relevant to the class you are testing. That is, the logger's state (and its possible changes) have no effect whatsoever on the state of the tested class. So it does not make your unit tests any more difficult.
The alternative would be to inject the logger via the constructor, to (almost) every single class in your app. For consistency of interfaces, it should be injected even if the class in question does not log anything at present - the alternative would be that when you discover at some point that now you need to log something from this class, you need a logger, thus you need to add a constructor parameter for DI, breaking all client code. I dislike both of these options, and I feel that using DI for logging would be just complicating my life in order to comply with a theoretical rule, without any concrete benefit.
So my bottom line is: a class which is used (almost) universally, but does not contain state relevant to your app, can safely be implemented as Singleton.