I think "Singletons considered harmful" is just a short way off now :-)
So 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
re "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
having 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.
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.
Singletons 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: Assert.AreEqual(DBNull.Value, m_target.Query());
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.
Some 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:
If 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.
While 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.
I 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."
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