Pejorative comments about singleton classes abound. All they seem to me to have proved is the age old truth of software: all techniques can be used badly. Earlier, I looked at Steve Yegge’s complaints about singletons. Now I’m reviewing a set of objections attributed to Brian Button by Scott Densmore.
FIrst, I have to point out that I’m writing specifically about PHP, and also that my views are colored by the kind of applications I write in PHP – the Aliro CMS and applications that can be installed into Aliro and other content management systems.
1) Singletons frequently are used to provide a global access point for some service. True, they do this, but at what cost? They provide a well-known point of access to some service in your application so that you don’t have to pass around a reference to that service. How is that different from a global variable? (remember, globals are bad, right???) What ends up happening is that the dependencies in your design are hidden inside the code, and not visible by examining the interfaces of your classes and methods. You have to inspect the code to understand exactly what other objects your class uses. This is less clear than it could be. The urge to create something as a global to avoid passing it around is a smell in your design; it is not a feature of globals/singletons. If you examine your design more closely, you can almost always come up with a design that it is better and does not have to pass around tramp data to every object and method.
There seems to be an awful lot of muddle in this paragraph, and also a lot of argument by name calling, something I never find convincing.
Let’s start with some of the name calling. On the whole, I share the aversion to global data. Its uncontrolled use creates interlocking dependencies in code that result in poor quality and difficult maintenance.
But I’m not convinced that global data is always wrong. Take the case of the absolute path that points to the home directory of a content management system. There is a good case for defining a symbol that has the value of that path. In PHP, this has benefits over the path being in any kind of variable, whether global or within an object. The biggest benefit is that a symbol in PHP can be defined only once, and thereafter cannot be altered. Opportunities for hacking are thereby significantly reduced by the use of what is, in effect, a read only value.
In most cases, though, I’d be doubtful about using global data simply because it is just that – raw data. And because typically it is not read only. I’m also cautious about using public properties in objects, or routinely using the overloading __get function to make properties available. On the whole, objects work best if they have interfaces that have been carefully thought out to be, so far as can be achieved, independent of the implementation inside the object.
A more rudimentary problem with the argument presented above is that class names just are global in PHP. So ANY use of classes involves global names, not just singletons.
2) Singletons allow you to limit creation of your objects. This is true, but now you are mixing two different responsibilities into the same class, which is a violation of the Single Responsibility Principle. A class should not care whether or not it is a singleton. It should be concerned with its business responsibilities only. If you want to limit the ability to instantiate some class, create a factory or builder object that encapsulates creation, and in there, limit creation as you wish. Now the responsibilities of creation are partitioned away from the responsibilities of the business entity.
Well, the advice given here can cut both ways. It is all very well to create an indirection via a factory of some kind, but the downside to this is that it is no longer possible to know for sure what is the relationship between the code using the object and the object itself. When you call a factory method, the code does not indicate what kind of object you will get; you do not know where to look in the code to find out how it will behave. Now, that may be an advantage in some situations, but in others it is just a huge cost.
A significant reason for building singletons is to have objects that know all about a particular area, maybe know all about a set of objects of some particular class. In this case, it is absurd to say that the class does not have a responsibility for worrying about its instantiation. The cost is high because a large amount of data has to be obtained and organized. There is no need for more than one instance of the data and callers could have no interest in obtaining multiple copies of the object.
Further, a factory has no way in PHP of enforcing the singular character of the object it returns. If an object is capable of being created by virtue of having a public constructor, then it can be created by anyone. It cannot be made private, because that prevents the factory from using it. And if the object in question is designed on the assumption that it exists only once, then it is desirable to enforce its singularity.
3) Singletons promote tight coupling between classes. One of the underlying properties that makes code testable is that it is loosely coupled to its surroundings. This property allows you to substitute alternate implementations for collaborators during testing to achieve specific testing goals (think mock objects). Singletons tightly couple you to the exact type of the singleton object, removing the opportunity to use polymorphism to substitute an alternative. A better alternative, as discussed in the first point above, is to alter your design to allow you to pass references to objects to your classes and methods, which will reduce the coupling issues described above.
This seems an exaggerated concern, which is in any case not directly linked with the singleton pattern. If what you want is an object that knows all about plugins, there is not really much scope for polymorphism. The object needs to honor a specific API that insulates other objects from the inner workings of the plugin system. Moreover, unless we have a very restricted view of what counts as a singleton, then a singleton class is not obliged to always return the same kind of object when asked to get its instance.
4) Singletons carry state with them that last as long as the program lasts. Persistent state is the enemy of unit testing. One of the things that makes unit testing effective is that each test has to be independent of all the others. If this is not true, then the order in which the tests run affects the outcome of the tests. This can lead to cases where tests fail when they shouldn’t, and even worse, it can lead to tests that pass just because of the order in which they were run. This can hide bugs and is evil. Avoiding static variables is a good way to prevent state from being carried from test to test. Singletons, by their very nature, depend on an instance that is held in a static variable. This is an invitation for test-dependence. Avoid this by passing references to objects to your classes and methods.
Yes, and singletons are only appropriate for situations where it is desirable for them to carry state for as long as the program lasts! But conversely, a singleton will always give back the same object to each caller, irrespective of the order of the calls. The mere fact of being a singleton is not a problem in itself, the real question is whether state is changing in the system in a way that invalidates testing. The advice to pass references to objects is no kind of answer, since the order of the calls to different methods may affect the state of the passed object, leaving the same problem of call sequence dependence that invalidates tests.