Dave's Space

A dimension not only of sight and sound, but of mind

Singletons considered harmful

2009-08-29 by David, tagged as oop, patterns

The trouble with people is that they tend to oversimplify things. It's in our nature. But, this trait can get us into trouble, especially when dealing with complex systems. When it comes to programming patterns, I submit that the singleton pattern is overused and is currently causing more harm than good. Stop the madness!


I have unfortunately been to too many job interviews, where the interviewer asked "So, are you familiar with patterns?", and the interviewee answered, "Yes, I know the singleton pattern!" -- end of discussion. The singleton pattern has unfortunately become synonymous with patterns in general. And although there are many many patterns out there which are in use, probably 2% of all patterns account for 98% of their use.

The singleton pattern is popular for one reason, and one reason only, it allows the user of the pattern to pull an instance "out of thin air" without having to go through all that nasty trouble of actually passing it into the constructor or through a method parameter.

Before I proceed, it is worth mentioning that the general population still hasn't come to terms with Object Oriented Programming (OOP). The concepts are out there, but not really used correctly. The idea that your objects should be re-usable is all but abandoned in most projects, and especially object encapsulation is violated at every turn.

We all know that static methods all over the place aren't a good thing. Why is that?

Now let's look at the singleton pattern. At first glance, it appears to avoid the static method traps because you are actually coding an object with real instance methods. In fact, it promotes procedural-style thinking in much the same manner as static methods. Here is an example of a singleton pattern, in the same way as it is implemented in 99.999999999% of all C++ projects.

class MySingleton
{
public:
    static GetInstance()
    {
        if( This == NULL )
            This = new MySingleton();

        return This;
    }

    void DoSomething() { /* Something amazing happens here */ }

private:
    MySingleton();
    static *MySingleton This;
};
MySingleton::This = NULL;

What could possibly be wrong with this??

Threading

The singleton pattern above, suffers from a severe threading issue which could crash your application. The GetInstance method is not thread-safe, it is possible that multiple MySingleton objects can be created. It is possible to make this method thread safe by way of a mutex, but now you also need a static mutex, and a way to statically initialize the mutex.

This forces your object to only have one instance

People that implement the above because of convenience, would do well to re-read the gang-of-four patterns book, where this pattern was introduced. The singleton pattern is meant to be used when you have to enforce that there is only one instance of a class. If there is no good reason for enforcing that rule, then it is much better to make, say, a static instance of your class somewhere instead of using the singleton pattern. Making it a singleton for convenience prevents another developer from making another instance, they might need to do this sometime! Please think about your design.

It is not possible to derive/extend this singleton easily.

If I want to modify the behavior of your singleton through extending it, as it is implemented above, it is not possible. Again, please re-read the singleton pattern in the gang-of-four books. They outline a number of possible methods whereby a user may extend a singleton, but I have NEVER seen ANY of these approaches adopted in practice. The result is that any singleton in practical use cannot be extended.

Classes which use the singleton are tightly coupled to it.

Because of the GetInstance method above, any class which uses this method must unfortunately use the scope-resolution operator pertaining to a specific class, i.e. MySingleton::GetInstance()->DoSomething(). This tightly couples the user of the class to the singleton and thereby violates object encapsulation. If you absolutely must use a singleton, please pass it in the contructor of your object, instead of calling it in the middle of some method somewhere. Then there is only one place that we need to change if we want to replace the singleton.

Singletons which use other singletons

If, in the "DoSomething" method above, we call another singleton, say MyOtherSingleton::GetInstance()->DoSomethingElse(), we have tightly bound these two singletons together and now they are mutually re-enforcing. Try untangling that ball-o-wax.

Initialization of singletons

After some time, you may find that you need to pass an argument into the constructor of your singleton. DON'T DO IT!! This introduces a number of sub-problems. Firstly, you will have to either make your constructor public, or you will have to pass an argument to the GetInstance method. If you put an argument on the GetInstance method, all of a sudden, everyone that is using your singleton will need to provide the argument. In order to avoid this, I have seen Initialize methods which are added to the singleton. The problem with this is that you have to elect a special object which initializes the singleton and thereby adds a dependency in terms of startup sequence. This violates one of the main points to a singleton, which is lazy instantiation.

De-initialization of singletons

As your application shuts down, you may find that you have created some singletons which have threads, and those threads need to be closed cleanly. But, whose responsibility is it to clean up these threads? If you just start deleteing objects, you can wind up with a nice crash-on-exit. Again, the solution so often seen is to have another "special class" which is responsible for shutting down the singleton. The problem with this is that singletons are called all over the place, and it might be invoked again after you've shut it down. So basically you have violated the principle that singletons can be invoked anywhere, anytime, and by anyone.