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.
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.
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"
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.
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.
My response to this comes in several parts:
getInstance()
method is bad, they why can't you say the same thing for the constructor and the destructor? Any principle can be stretched to ridiculous lengths, so when do you stop?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.
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.
By this you mean unit testing using mock objects. I don't use mock objects, so I don't have this problem.
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.
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.
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.
_getDBMSengine()
method which is defined within the abstract table class which is inherited by every table class. This method combines information obtained from both the config.inc file and the table class to identify which of the supported DBMS engines is required for that connection, then it calls the singleton::getInstance()
method to obtain a singleton instance of the class which is responsible for that DBMS._getDBMSengine()
method to prefix the class name with the server name in the format server__N__<class>
. This is then processed in the getInstance()
method by creating an instance of <class>
but storing it in the $instances
array with the name of server__N__<class>
._getDBMSengine()
and getInstance()
methods to act as if the connection for the serial read was needed on a different server, which meant that the two operations could be performed on different connections, thus avoiding the error.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.
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.
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.
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".
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.
These are reasons why I consider some ideas on how to do OOP "properly" to be complete rubbish: