— “Do you know any Singleton jokes?”
— “Just one.”
Singleton is one of the simplest patterns to understand. The main goal of it is to limit the existence of only one instance of the class. The reason for it is usually the following:
only one class object is required during the lifecycle of the application and you need this object to be available anywhere in the application.
The Singleton pattern assumes that there is a static method for getting an instance of the class (
getInstance()). When calling it a reference to the original object is returned. This original object is stored in a static variable, which allows keeping this original object unchanged between
getInstance() calls. Also, a constructor is
private to ensure that you always use only a static
getInstance method to get the object. In PHP we have some magic methods which can be used to create a new instance of the class:
__wakeup, they also should be
This pattern can be useful when we have some kind of a shared resource in our application: a classic example is a database connection. Different parts of the application might want to use this connection.
The problems with Singleton comes when we start using them as global instances. But the main problem is not with the globals, but how we use them. A single instance doesn’t actually mean globally accessible. The common mistake is to always access to an instance of the singleton directly via its static
Consider a classic Singleton example with a database connection:
And then in our application, we start using it to perform queries like this:
In this case, our
QueryBuilder is tightly coupled to
DB class. We cannot use
QueryBuilder without calling
DB class. It is now impossible to test
QueryBuilder without actually touching the database. Because of the hardcoded dependency, we cannot mock
DB class with some fake connection.
A database is actually not a good example for a Singleton. For example, a client wants to connect to the same database but with different credentials. Or a client wants to connect to several databases.
To fix this issue we should use Inversion of Control and pass singleton as a dependency, just like you would do it with any other object. Most of our code shouldn’t event know it is dealing with a Singleton. For testing purposes we can also create the
Connection interface, so we could mock it:
QueryBuilder should depend on the
QueryBuilder completely depends on the database connection, so in our case, we can pass an instance of the database connection as a constructor dependency.
No more static calls and hardcoded dependencies. We can easily mock database connection and test
QueryBuilder in isolation.
QueryBuilder even doesn’t know that it collaborates with a Singleton. With this approach, you may think that now we have to pass around a reference to a Singleton instance everywhere in our application, so actually, we don’t have a Singleton anymore. But do you remember the main purpose of the Singleton? It is not a global state and not a static access, but providing only one instance of the class.
Singleton And Tests
With the only one instance of the class, we can have some problems in testing. Our tests may become dependable on each other because Singleton stores its state during the tests. If one test changes the state of the Singleton instance, the other test cannot start from scratch and has to deal with this changed state. Consider this simple logger class:
If we start testing this class we can face some unexpected results:
This test may fail if somewhere in other tests we have already logged something. There is a good recipe how to fix this issue in the book Working Effectively with Legacy Code. The author advices to introduce a
setInstance() method, which allows to replace the static instance of the Singleton:
This allows us to mock the Singleton. Another option is when we need to reset the state, especially when testing the Singleton itself:
reset() simply overrides the current state of the Singleton, so we can start from scratch. Then in our tests, we can use
setUp method to
reset Singleton’s state before each test:
In practice, the Singleton is just a programming technique, which can be a useful part of your toolkit. Singletons themselves are not bad, but they are hard to do right. We always consider singletons as globals. Singleton is not a pattern to wrap globals. The main goal of this pattern is to guarantee that there is only one instance of the given class during the application lifecycle.
Don’t confuse singletons and globals. When used for the purpose it was intended for, you will achieve only benefits from the Singleton pattern. Simply in most cases rather than teaching good examples of how to do Singletons we have tons of tutorials where we show bad examples and then later we make a conclusion that singleton is a bad design pattern.