Root Cause of Singletons
Wednesday, August 27, 2008
by Miško Hevery
Since I have gotten lots of love/hate mail on the Singletons are Pathological Liars and Where Have All the Singletons Gone I feel obliged to to do some root cause analysis.
Lets get the definition right. There is Singleton the design pattern (Notice the capital "S" as in name of something) and there is a singleton as in one of something (notice the lower case "s"). There is nothing wrong with having a single instance of a class, lots of reasons why you may want to do that. However, when I complain about the Singletons, I complain about the design pattern. Specifically: (1) private constructor and (2) global instance variable which refers to the singleton. So from now on when I say Singleton, I mean the design (anti)pattern.
I would say that at this point most developers recognize that global state is harmful to your application design. Singletons have global instance variable which points to the singleton. The instance is global. The trouble with global variables is that they are transitive. It is not just the global variable marked with static which is global but any other variable/object which is reachable by traversing the object graph. All of it is global! Singletons, usually are complex objects which contain a lot of state. As a result all of the state of Singleton is global as well. I like to say that "Singletons are global state in sheep's clothing." Most developers agree that global state is bad, but they love their Singletons.
The moment you traverse a global variable your API lies about its true dependencies (see: Singletons are Pathological Liars) The root problem is not the Singleton design pattern, the root problem here is the global reference to singleton. But the moment you get rid of the global variable you get rid of the Singleton design pattern. So from my point of view blaming Singletons or blaming global state is one and the same. You can't have a Singleton design pattern and at the same time not have the global state.
Someone pointed out that any design pattern can be abused. I agree, but with Singleton design pattern, I don't know how I can possibly use it in a good way. The global reference and hence the global state is ever so present. Now, in my line of work I don't see too much global state in classical sense of the word, but I see a lot of global state masquerading as Singletons. Hence, I complain about Singletons. If I would complain about global state no one would care, as that is old news.
Now, there is one kind of Singleton which is OK. That is a singleton where all of the reachable objects are immutable. If all objects are immutable than Singleton has no global state, as everything is constant. But it is so easy to turn this kind of singleton into mutable one, it is very slippery slope. Therefore, I am against these Singletons too, not because they are bad, but because it is very easy for them to go bad. (As a side note Java enumeration are just these kind of singletons. As long as you don't put state into your enumeration you are OK, so please don't.)
The other kind of Singletons, which are semi-acceptable are those which don't effect the execution of your code, They have no "side effects". Logging is perfect example. It is loaded with Singletons and global state. It is acceptable (as in it will not hurt you) because your application does not behave any different whether or not a given logger is enabled. The information here flows one way: From your application into the logger. Even thought loggers are global state since no information flows from loggers into your application, loggers are acceptable. You should still inject your logger if you want your test to assert that something is getting logged, but in general Loggers are not harmful despite being full of state.
So the root cause is "GLOBAL STATE!" Keep in mind that global state is transitive, so any object which is reachable from a global variable is global as well. It is not possible to have a Singleton and not have a global state. Therefore, Singleton design patter can not be used in "the right way." Now you could have a immutable singleton, but outside of limited use as enumerations, they have little value. Most applications are full of Singletons which have lots of global state, and where the information flows both directions.
Since I have gotten lots of love/hate mail on the Singletons are Pathological Liars and Where Have All the Singletons Gone I feel obliged to to do some root cause analysis.
Lets get the definition right. There is Singleton the design pattern (Notice the capital "S" as in name of something) and there is a singleton as in one of something (notice the lower case "s"). There is nothing wrong with having a single instance of a class, lots of reasons why you may want to do that. However, when I complain about the Singletons, I complain about the design pattern. Specifically: (1) private constructor and (2) global instance variable which refers to the singleton. So from now on when I say Singleton, I mean the design (anti)pattern.
I would say that at this point most developers recognize that global state is harmful to your application design. Singletons have global instance variable which points to the singleton. The instance is global. The trouble with global variables is that they are transitive. It is not just the global variable marked with static which is global but any other variable/object which is reachable by traversing the object graph. All of it is global! Singletons, usually are complex objects which contain a lot of state. As a result all of the state of Singleton is global as well. I like to say that "Singletons are global state in sheep's clothing." Most developers agree that global state is bad, but they love their Singletons.
The moment you traverse a global variable your API lies about its true dependencies (see: Singletons are Pathological Liars) The root problem is not the Singleton design pattern, the root problem here is the global reference to singleton. But the moment you get rid of the global variable you get rid of the Singleton design pattern. So from my point of view blaming Singletons or blaming global state is one and the same. You can't have a Singleton design pattern and at the same time not have the global state.
Someone pointed out that any design pattern can be abused. I agree, but with Singleton design pattern, I don't know how I can possibly use it in a good way. The global reference and hence the global state is ever so present. Now, in my line of work I don't see too much global state in classical sense of the word, but I see a lot of global state masquerading as Singletons. Hence, I complain about Singletons. If I would complain about global state no one would care, as that is old news.
Now, there is one kind of Singleton which is OK. That is a singleton where all of the reachable objects are immutable. If all objects are immutable than Singleton has no global state, as everything is constant. But it is so easy to turn this kind of singleton into mutable one, it is very slippery slope. Therefore, I am against these Singletons too, not because they are bad, but because it is very easy for them to go bad. (As a side note Java enumeration are just these kind of singletons. As long as you don't put state into your enumeration you are OK, so please don't.)
The other kind of Singletons, which are semi-acceptable are those which don't effect the execution of your code, They have no "side effects". Logging is perfect example. It is loaded with Singletons and global state. It is acceptable (as in it will not hurt you) because your application does not behave any different whether or not a given logger is enabled. The information here flows one way: From your application into the logger. Even thought loggers are global state since no information flows from loggers into your application, loggers are acceptable. You should still inject your logger if you want your test to assert that something is getting logged, but in general Loggers are not harmful despite being full of state.
So the root cause is "GLOBAL STATE!" Keep in mind that global state is transitive, so any object which is reachable from a global variable is global as well. It is not possible to have a Singleton and not have a global state. Therefore, Singleton design patter can not be used in "the right way." Now you could have a immutable singleton, but outside of limited use as enumerations, they have little value. Most applications are full of Singletons which have lots of global state, and where the information flows both directions.
what's your take on Spring transaction manager (http://static.springframework.org/spring/docs/2.5.x/reference/transaction.html ) ? no good, huh ?
ReplyDeleteI think "Singletons considered harmful" is just a short way off now :-)
ReplyDeleteSo how would one go about getting rid of Singletons? I use them for stuff that, well, only needs 1 instance. I would have to pass a non-singleton through to the method or function that needs it. Typically the reference would end up getting passed through several classes and calls. A Singleton short circuits that by popping up right were it is needed. e.g. a simple example:
class B {
void doMoreStuff() {
C::instance().setSomething();
}
}
class A {
void doStuff() {
b->doMoreStuff();
}
}
int main() {
A a;
a.doStuff();
}
Typically if A is the runner or controlling class, there's only 1 of it anyway, created in the main(). So adding a C instance member variable to A means you would have to pass it in everywhere that needed to use it. i.e. after de-singletonising:
class B {
void doMoreStuff(C *c) {
c->setSomething();
}
}
class A {
A(): c_(new C) {}
void doStuff() {
b->doMoreStuff(c_);
}
C *c_;
}
int main() {
A a;
a.doStuff();
}
Or create all the singletons in main() and inject them into the A instance's constructor with a factory. But in the end the no-longer-a-singleton has to get to where it's used, and that can be a real pain. Singletons make life easier in these cases; a guilty pleasure! I think the answer to "what can be done?" will inevitably be "it depends".
Misko talks about this concern here: http://www.youtube.com/watch?v=-FRm3VPhseI#t=31m20s
Deletere "inject them into the A instance's constructor": Not necessarily. In your specific example, A does not collaborate with C, not directly anyway; A directly collaborates with B only. Therefore, you should instead do something like this:
int main() {
C c;
B b(c);
A a(b);
}
There isn't that much passing around of c. Yes, c needs to be passed to b. This is a Good Thing(TM), because it is now clear that B depends on C. Singletons obfuscate this fact. Misko also talks about this problem earlier in the same video: http://www.youtube.com/watch?v=-FRm3VPhseI#t=18m52s
@Quirky
ReplyDeletehaving a singleton directly where you needs it without having to pass it thru many layers is indeed really seductive.
From my experience, it only prooves that you have a bad architecture to begin with.
Sometimes the architecture is something you can't change, so singleton may have some use there.
But mark my words, singleton are nasty boomerangs, they will come back in your face at full speed.
I have never seen a Singleton survive in big code base focused on quality, in the long run, they are simply to dangerous/painful. They can exist during a period of time, but they are replaced ASAP, before they rigidify the system around them.
For example, singletons and multicore/multiprocesses are really really something you don't want to mix.
No that I disagree that prolific referencing of global data (i.e. abuse of the singleton pattern) leads to brittle / hard to test software ...
ReplyDeleteThe irony here is that the vast majority of IoC containers require heavily on use of the singleton pattern themselves ;)
Think ..
ContextRegistry.GetContext().GetObject("MyType")
in Spring.NET for instance ;)
As you said, I think "most developers recognize" that old-school global variables (like those heavily abused in C) are a blight. But this is really a bit of a nostalgic argument, and I don't know that it's directly relevant to Singletons.
ReplyDeleteSingletons can certainly be abused, but I'd say they're one step up from C-style global vars. At least they let us categorize global vars and behaviors into classes, to give hints as to purpose and improve serviceability.
I think the core problem really stems from (what is incidentally your primary viewpoint) testability concerns. And from there I can agree with you, and have been reviewing your previous articles for suggestions on how to improve this issue.
Nonetheless, I think it's a mistake to wholly throw out Singletons as remnants of caveman code. They have their place, and they are an improvement over what "most developers" think of as global state.
In my experience, i found singleton pattern useful for ValueObjects (aka immutable objects) or objects witout state. An example is .NET DBNull.Value, string.Empty and CultureInfo.InvariantCulture. You can use/make assertion based on the same instance also on your tests, so:
ReplyDeleteAssert.AreEqual(DBNull.Value, m_target.Query());
Bye
Let's for the sake of argument say that Singleton is considered harmful. Just as Quirky asked, what can be done? Some concrete tips would be really handy I think.
ReplyDeleteSome Global states aren't bad. Some times you need inherit in constant values. And you can't make static values be inherited. So, some times is the solution not the design. I will give you a simple example:
ReplyDeleteIf u have a class Enterprise, and you want that everybody that inherited of Enterprise has some properties/methods implemented and these methods are constants for each class. So what you gonna do? Singleton! :)
Generally I agree with your points here, but it may be harmful in itself to make it canon that "Singletons are bad". Like any design pattern, they are an antipattern the moment that they're applied in the wrong context. However, there are plenty of instances where the mistake would be doing the opposite--state machines like OpenGL and OpenAL, as an example, only ever provide a single instance because they are tightly (and literally) bound hardware; a second GL object will not give you a second graphics system in the real world. Thus, making a GL object factory is a terrible and misleading idea. Anything bound to a singular real-world state machine basically requires either a singleton or a static class, and for me, singletons have been much more convenient. Additionally, if one is working with something highly resource-constricted, like cell phone or microcontroller source, the overhead of multiple objects can be crippling when only one is necessary, and decency suggests a Singleton restriction. There are instances where even global objects have their place, albeit rare ones.
ReplyDeleteWhile the singleton pattern is often abused in places where a factory might be more appropriate, it is important to remember that it is most frequently applied in instances in which one is creating an API or toolkit. There are things that we don't want the next programmer to use the kit to have to think about, and often, those things have the necessity in them of only a single instance of a class existing.
But still, with say opengl you could just create an instance and pass it around to classes that need it. If you define it as a global variable instead then you have classes that 'secretly' access it. i.e. they don't tell you they access it in their interface, they just do it.
DeleteI hear the "bound to hardware" argument a lot, but it's not actually very helpful. Hardware can change--not as fast as software, perhaps, but faster than API standards committees! Saying that "there will only be one of these" is the same as saying "my code is not future-proof."
DeleteWhat are your views on Singleton VS Dependency Injection? (Ala Angular)
ReplyDelete