Tony Marston's Blog About software development, PHP and OOP

Singletons are NOT evil

Posted on 1st December 2017 by Tony Marston
Introduction
The problems with singletons
I don't have those problems
Conclusion
References
Comments

Introduction

I have been told many times that singletons are bad, and that because I use singletons in my code then it must also be bad. When I ask why singleton's are bad I am usually told that "Everyone knows that they are bad, so don't ask stupid questions". This tells me straight away that the person doesn't actually know why singletons are supposed to be bad, he is just acting as an echo chamber for what he has heard on the grapevine. When I eventually came across articles which explained the problems that are caused by singletons one simple fact jumped out at me - everyone assumes that there is only one way to implement the singleton pattern. While it is true that a particular implementation of a pattern may have problems, it is wrong to assume that only a single method of implementation exists, and that the same problems exist in all those other implementations.

The earliest description of the singleton pattern can be found in the GoF book which states the intent as:

Ensure a class has only one instance, and provide a global point of access to it.

It does NOT specify that the class itself must provide the single instance of its object, so it does not specify a particular implementation which will satisfy its needs. This means that the following implementation, while quite common, should not be treated as the only allowable implementation:

Illustration 1 - a getInstance() method within each class

<?php
class foobar
{
    private static $instance;
		
    public static function getInstance()
    {
        if (null === self::instance) {
            self::instance = new self()
        } // if
        return self::instance;
			
    } // getInstance
		
    ....
		
} // foobar

To get a single instance of class foobar all you need do is call $foobar = foobar::getInstance(). Simple isn't it?

Being a heretic I don't follow the crowd, so I choose a totally different implementation, as described in The Singleton Design Pattern for PHP and as shown in illustration 2. This is where I have a single static getInstance() method inside a custom made singleton class.

Illustration 2 - a single Helper method with class loader

<?php
class singleton
// ensure that only a single instance exists for each class.
{
    static function &getInstance ($class, $arg1=null)
    // implements the 'singleton' design pattern.
    {
        static $instances = array();  // array of instance names

        if (array_key_exists($class, $instances)) {
            // instance exists in array, so use it
            $instance =& $instances[$class];
            
        } else {
            // load the class file (if not already loaded)
            if (!class_exists($class)) {
                switch ($class) {
                    case 'date_class':
                        require_once 'std.datevalidation.class.inc';
                        break;

                    case 'encryption_class':
                        require_once 'std.encryption.class.inc';
                        break;

                    case 'validation_class':
                        require_once 'std.validation.class.inc';
                        break;

                    default:
                        require_once "classes/$class.class.inc";
                        break;
                } // switch
            } // if

            // instance does not exist, so create it
            $instances[$class] = new $class($arg1);
            $instance =& $instances[$class];
        } // if

        return $instance;

    } // getInstance
    
} // singleton

Note that if the class definition is not already loaded it will be obtained using the require_once function which will search through those directories which were specified in the include_path configuration setting. Note also that every one of my domain/business objects is a database table where the class name is the same as the table name, the class file is called <tablename>.class.inc and it always exists in the <subsystem>/classes directory where <subsystem> is the name of the database. This allows my application to access any number of databases, and the code for each database is kept separate in its own directory. Other non-database classes exist in the <includes> directory and may have file names which do not follow the same naming pattern as the table classes.

Note that the current implementation of my singleton class, as available in the download of my RADICORE framework, has grown quite a bit over the years in order to cater for different sets of circumstances.

The problems with singletons

Here is a list of the problems which various people say exist with the single pattern. As they do not specify which implementation of the pattern has these problems, the dumb schmucks automatically assume that every implementation has the same set of problems.

  1. They pollute global namespace.
  2. They carry state with them that last as long as the program lasts.
  3. They violate the Single Responsibility Principle (SRP).
  4. They break encapsulation.
  5. They promote tight coupling between classes.
  6. They are hard to test.
  7. They cannot be subclassed.
  8. They cannot handle multi-threading.
  9. They do not allow you to create more than one instance.
  10. They do not allow different users to have different configurations
  11. They kill polymorphism since they make it impossible to swap implementations
  12. They break the Open/Closed Principle (OCP)
  13. They break the Dependency Inversion Principle (DIP)

I don't have those problems

When I look for each of those so-called "problems" in my own implementation I cannot find any of them. If my implementation does not have the problems which cause this pattern to be labelled as "bad", then surely it is not bad at all? Below are my answers to the so-called "problems"

  1. They pollute global namespace

    Everyone knows that you are not supposed to use global variables, right? WRONG! The singleton is supposed to provide a global point of access, and the use of the word global says it all. It MUST exist as a global as it MUST be available from anywhere. Besides, using a global variable is NOT the same as over-using, mis-using or ab-using, as explained in Levels of use.

  2. They carry state with them that last as long as the program lasts

    You need to remember that in PHP each "program" only lasts as long as the time it takes to provide a response to an HTTP request, so there is no such thing as a global variable which sits around for long periods. When a PHP script is run it starts with no state whatsoever, global or otherwise. Any globals which are created are done so within the execution of that script so it shouldn't be too difficult to track down where they were created. When the script terminates all resources are dropped, so there is nothing to be carried forward.

    I should also point out that none of my singleton objects contains state that needs to be different between one invocation and another. Each time I call a method on a singleton object, such as my date formatter, it generates a result which is passed back to the caller and any internal state which is left over is irrelevant as it will be overwritten the next time that object is called.

    It is also said that applications which rely on global state are hiding their dependencies. None of my singleton objects are dependent on other objects, so this problem does not exist in my world.

  3. They violate the Single Responsibility Principle (SRP)

    My response to this comes in several parts:

  4. They break encapsulation

    I do not see the link. Encapsulation is all about putting related properties and methods in the same class, whereas a singleton is concerened with obtaining a single instance of that class. The two terms are unrelated, so this claim is completely bogus and without merit.

  5. They promote tight coupling between classes

    Only if you use a perverted definition of coupling. If one module interacts with another then there definitely is coupling between those two modules, but it is the degree of coupling which decides whether they are tightly or loosely coupled. As all my method calls have been specifically designed to use a simple and stable interface they match the definition of low coupling, and the method by which the object was instantiated cannot change that.

    Some people seem to think that low coupling is only achieved when you can substitute alternative implementations of a method at run-time, but this is a load of bollocks balderdash. That ability is called polymorphism, which is something else entirely.

  6. They are hard to test

    By this you mean unit testing using mock objects. I don't use mock objects, so I don't have this problem.

  7. They cannot be subclassed

    This only applies when you have a separate getInstance() method within each class, in which case a call to this method in the superclass from a subclass will return an instance of the superclass instead of the subclass. I don't use this implementation, so I don't have this problem. Instead I have a single getInstance() method inside a singleton class which has the class name as its first argument. This means that it will always create an object from that class name, and it won't care if that is a subclass or not.

  8. They cannot handle multi-threading

    PHP was designed to be used for creating web pages, and you don't need multi-threading capabilities to create web pages. This "problem" is therefore totally irrelevant.

  9. They do not allow you to create more than one instance

    With my implementation as each class is not responsible for its own instantiation, it is entirely up to the caller as to how that class is instantiated. If I want a singleton instance then I use $object = singleton::getInstance('<class>'), but if I want a non-singleton instance then I use $object = new '<class>' instead. Easy peasy lemon squeezy.

    The most common description I have seen which explains this problem has to do with a database connection, but the actual problem is caused by when and how they obtain a single instance of that connection. They create an singleton instance of the Data Access Object (DAO) in the presentation/UI layer, then use dependency injection to inject this instance into the business layer. The business layer can then only access the single connection that it has been given, which causes great problems should your application ever require an additional database connection. My implementation still uses singletons, but in a way that totally avoids any of these problems.

  10. They do not allow different users to have different configurations

    Firstly, it should be remembered that PHP uses the shared-nothing architecture, which means that each script starts with a blank canvas, and when it terminates everything in memory is lost. Nothing is shared between one web page and another. This means that any singleton which is created will automatically die when the script terminates.

    Secondly, if there are places within the execution of a single script that require a different configuration of the same object, then that object should not be a singleton. If you declare something to be a singleton when it should not be, then it's your fault and not the fault of the singleton.

  11. They kill polymorphism since they make it impossible to swap implementations

    Then you do not understand how a singleton is supposed to be used. It is a single instance of a single class where the class name is known in advance, and you do not want to create a duplicate instance of that class.

    In the case where the class name is not known until run-time then my implementation can be used as follows:

    <?php
    $class_name = "foo";
    $object = singleton::getInstance($class_name):
    $result = $object->getData();
    ?>
    

    Note here that I can insert some clever code to work out what the name of the class should be, and the code will provide me with a singleton instance of that class. If I call it again with the same class then I get the same instance, if I call it with a different class then I get a different instance. This is how I have solved the problem of obtaining more than one database connection.

  12. They break the Open/Closed Principle (OCP)

    The argument for this is that the singleton class itself is in control over the creation of its instance, while others have a hard dependency on it. This disallows the implementation to be changed for another one, without having to make sweeping changes throughout the application. In my implementation each class is not responsible for its own instantiation, so this argument does not apply.

  13. They break the Dependency Inversion Principle (DIP)

    The argument for this is that consumers will always depend directly on the concrete class to get the instance, while the DIP says that we should depend on abstractions. In my implementation each class is not responsible for its own instantiation, so this argument does not apply.

You should notice that my implementation does not have any of the problems reported by those who insist on using inferior implementations, so if I don't have any problems then my implementation cannot be classed as "bad".

Conclusion

When somebody says "Singletons are bad" you should ask them "Which implementation?". If they cannot answer that question because they have assumed that only one implementation is possible then you have my permission to give them a smack on the back of the head. You should instead tell them "Global statements which fail to take account of local variations are bad". That should shut the buggers up.

References

The following articles can all be found on tonymarston.net.


counter