Login with OpenID

Why Singletons Are Evil

Written by Hector Virgen
Published on August 26, 2010
Last updated on August 26, 2010

One of the nice things about design patterns is that once you learn them they are really quite easy to implement. One of the simplest design patterns is the Singleton. Not only does it comprise of a single class, but it can be built and used very quickly.

The basic idea behind a Singleton is to limit the number of instances allowed throughout the application. Usually they're limited to a single instance (hence the name "Singleton") but can be modified to support any number of instances.

Here's a Singleton implementation in PHP:

class MySingleton {
    protected static $_instance;
    
    protected function __construct() {}
    
    protected function __clone() {}
    
    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }
}

Starting at the top, we have a single static property called $_instance. This will eventually hold our singleton instance.

Continuing on, you'll notice that __construct() and __clone() are protected. This prevents these methods from being called from anywhere outside the class. In other words, you won't be able to call the code new MySingleton(); unless you're inside the class. The same is true for cloning -- it can only be done inside of the class. Any attempt to instantiate or clone this class from the outside will cauase a fatal error in PHP.

$mySingleton = new MySingleton();
// Fatal error!

Lastly, there is a static getInstance() method. Note that it's marked public, meaning it can be called from the outside. Inside this method, it checks the $_instance property to see if its null and, if it is, lazy-loads the instance into that property.

$mySingleton = MySingleton::getInstance();
// This works!

If you were to call that code again later, the same instance would be returned instead of a new one. This pattern can be used to ensure that the application isn't creating multiple objects unneccessarily. For example, you probably don't want multiple logger instances throughout your app if they're all writing to the same log.

This pattern is as elegant as it is simple, but it comes with a price.

One of the major problems with singletons is that they are very sneaky -- just like global variables. Consider this portion of a class that uses a database singleton:

class Users
{
    public function fetchAll()
    {
        // Singleton access to DB
        $db = MyDatabase::getInstance();
        
        // Fetch all users
        return $db->fetchAll('SELECT * FROM users WHERE 1');
    }
}

From the outside world, a client developer would probably write code something like this:

$users = new Users();
$allUsers = $users->fetchAll();

While that looks simple enough, the client developer has no idea that a database is involved unless she examines the source code. This is what's known as a hidden dependency.

Additionally, this makes the Users class very difficult to unit test. How would you write a test that uses a mock database? There's no way to inject a mock database unless you create a duplicate MyDatabase class.

A better alternative would be to require that all dependencies be injected instead of statically loaded:

class Users
{
    protected $_db;
    
    public function __construct(MyDatabase $db)
    {
        $this->_db = $db;
    }

    public function fetchAll()
    {
        // Fetch all users
        return $this->_db->fetchAll('SELECT * FROM users WHERE 1');
    }
}

Let's see how this changes the client's code:

$db = MyDatabase::getInstance();
$users = new Users($db);
$allUsers = $users->fetchAll();

What a difference! Now it's obvious that a database is involved without having to look at the source of the Users class. Additionally, it provides us with a way to inject a mock database for unit testing.

But we're still using a singleton, and it's all too tempting to revert back to sprinkling our classes with calls to its getInstance method. Additionally, when unit testing, it becomes a task to have to reset all of our singletons between each test. Is there a better way?

Lately I've been avoiding singletons entirely and sticking to basic classes. In order to ensure that only one instance is used, I bootstrap that instance. For example, in the Zend Framework I create a bootstrap resource class that can load my single instance. Whenever I need the resource, I pull it from the bootstrap and inject it into the class that needs it. This means I can inject stubs into the bootstrap to test my application and don't have to worry about hidden dependencies causing failures.

Comments

blog comments powered by Disqus