Very often patterns become so ingrained into a programmer’s thinking that they fail to question why they exist in the first place. Like a language feature that gets slowly phased out as people realize the problems inherent with the design, patterns are simply conventions of thought that should be continually criticized. Adderio et al.[1] describes this best, by pointing out that “Patterns are only a reduced, abstracted, subjectively filtered version of someone else’s knowledge and experience”. Often, without a critical filter, students gratuitously apply common patterns, believing they are best practices in all cases.
The idea for the article came from being involved in a group project where a team member insisted on using singletons to solve a particular problem. Having never explicitly implemented singletons myself, I did some research and stumbled on an age old debate over their usage. I was convinced that singletons were not necessary and could even be detrimental in the long run.
Though the following sections focus on why using a Singleton pattern is completely unnecessary in most programs, it serves to prove a larger point on a common belief that design patterns provide solutions rather than just suggestions. Although singletons have fallen out of fashion, I want to resurface the debate because it provides a cautionary tale for the blind adoption of patterns.
The Singleton
Singleton patterns were most famously explained in the seminal book ‘Design Patterns: Elements of Reusable Object Oriented Software’ [5]. Outside of this text, it is commonly explained in a one-liner; a singleton is ‘a class with at most one instance and with a global point of access.”
The example below (in java) gives the clearest representation of the concept (NB: there are safer and more efficient ways to create them using enums and static inner classes).
I believe the original intent of a singleton was to avoid expensive and unnecessary duplication by guarantying single instantiation in the class itself. However, as with most misunderstood patterns, the purpose was lost in translation and people began using them to replace global variables [6].
However, due to ‘the single point of access’, singletons do not avoid any of the dangers that are inherent in using globals. The first of these is an unwitting state change, where the global or singleton is modified in a section of code, but another function assumes it has not changed. Multiple references to the same object is another issue stemming from a same idea, where a local variable name may mask a reference to a global variable (e.g. when it’s passed as a parameter). Thirdly, it goes against the principle of modularity since classes are tied together by dependencies to the global / singleton and you can no longer re-use them in other programs. Furthermore, the dependencies in the design can no longer be deduced from looking at the interfaces of the classes and methods, which can lead to confusing bugs if the program grows in complexity. The drawback also trickles through to unit testing since tight coupling to the environment makes the use of mock objects difficult without making modifications to the code.
Consider the Alternatives
Considering why singletons exist in the first place, it’s perfectly reasonable to require a single store for data in a program and for code sections to access the same data. However, singleton patterns are not the best way of doing this.
Let’s separate the two goals of a singleton – ‘single instantiation’ and providing ‘a global point of access’.
As is it usually implemented (above example), the single instance feature is defined as a behavior of the class itself. However, in most cases having exactly one instance of the class is a requirement of the application. Implementing a singleton in these case fundamentally contradicts the ‘single responsibility principle’ of OOP, whereby a class should only worry about the one business functionality it was created to perform. Put another way, it should have ‘only one reason to change’ [7], whereas singletons change if the behavior of the class needs to be modified or if we later discover multiple instantiations maybe necessary. For example, singletons most commonly still appear in the design of logger classes to provide global access to a log-file without the expense of creating and closing new file-access objects[8]. The intent is sound, but there is nothing inherent about the logger class that says only one can exist. In fact, it is conceivable that a different application will want to reuse the logger class to instantiate two types of logs. So why not give this responsibility of instantiation to the application and essentially reduce the singleton to a normal class? This makes far more sense to me.
The ‘global point of access’ can be implemented simply by using a globally accessible method that passes an instance of the class to the one that requested it. Although the problems of global data structures still persist, now you can choose when global points of access will be appropriate for a particular program rather than it being enforced in a singleton class. For example, we could implement a builder object (i.e. a factory pattern) to either create an instance and reuse it in any function that requests it, or create new instances and pass those to requesting functions. By encapsulating the creation in another class, we’ve also separated the responsibility of global access from actual behavior of the singleton class.
Even when a singleton pattern seems like the best option, it’s important to understand why and consider the alternatives available. Usually when multiple instances of the class will break the program, it’s because there exists properties in the class that shouldn’t be duplicated. Instead of making the whole class a singleton, make the properties static to the class and thus invariant to new instantiations. Using private static properties and public getters and setters, which can also be made thread safe, we can mimic the intention of a singleton without compromising on the points made above.
Conclusion
After a surge of popularity, the singleton pattern was slowly phased out of best practices as developers realized its problems and considered alternatives. In the case of my project, after a little convincing we decided to use a normal class and factory because they would be more flexible and save headache down the line.
The simple patterns that developers learn first are usually the ones that are least likely to be questioned as they gain more experience. However it’s imperative that the idea and the logic behind the pattern is understood to the extent that we avoid their abuse and look to improve their design.
REFERENCES
[1] Adderio L., Dewar R, Stevens A. Lloyd A. (2002) “Has the Pattern Emperor any Clothes? A controversy in three acts”, accessed on 10/3/2014 at < http://www.dl.acm.org/citation.cfm?id=1148026>
[2] David Geary (2011), “Simply Singleton”, accessed on 9/3/2014 at http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpatterns.html
[3] Deru (2010) “Why Singletons are Controversial” accessed on 11/03/2014 at https://code.google.com/p/google-singleton-detector/wiki/WhySingletonsAreControversial
[4] Etheredge J. (2008), “The Monostate Pattern”, accessed on 12/03/2014 at http://www.codethinked.com/the-monostate-pattern
[5] Gamma E., Helm R., Johnson R., Vilssides J. (1994), “Design Patterns: Elements of Reusable Object-Oriented Software”, Addison-Wesley
[6] Miller A. “Patterns I hate” (2007), accessed on 12/3/2014 at < http://tech.puredanger.com/2007/07/03/pattern-hate-singleton/>
[7] Martin, R. (2002). “Agile Software Development, Principles, Patterns, and Practices”. Prentice Hall.
[8] OODesign Guide (2010), accessed on 10/3/2014 at < http://www.oodesign.com/singleton-pattern.html>