Thursday, 17 October 2013

Are singletons just misunderstood?

In around 2002 I read the Gang of Four [GangOfFour] and discovered the Singleton Pattern [SingletonPattern]. The gang of four describe the intent of the singleton pattern as:

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

Like most green developers I thought it was brilliant and used it to hold the database connection for my application as it was expensive to create, I didn’t want to create it until it was needed and I used it everywhere (my application was poorly abstracted).

I’d been a member of the ACCU [ACCU] for a little while and had made quite a few friends there who all told me Singletons were bad. I couldn’t see the problem and continued using it happily and didn’t think the extra compile and link time (I was using C++) when I modified the singleton was a problem. Until one day, when we introduced a second database for the application and I needed another version of the same singleton.  I didn’t see at the time that I could have used one singleton to serve up both database connections, but if I had I would have soon found just how tightly coupled to the singleton’s access function my code was.

The tones of “we told you so” are still resonating eleven years later and I have avoided singletons like the plague in favour of Kevlin Henney’s Parameterise From Above [PfA], where you create an object once in one place (for example in the main method) and then pass it around your code to every other object that needs it. Of course this doesn’t ensure that there is only one instance, it relies on the understanding and discipline of the developer.  It’s served me well for many years, but there have been a few isolated occasions since where the language or library I was using has restrictions where only a singleton would allow me to achieve what I needed to achieve. Happily this has mostly been in integration test code.

At a recent Norfolk Developers [NorfolkDevelopers] I chaired a BBC Question Time like panel on software development. The questions were very much based on my own views and prejudices about software engineering, so naturally there was a question on singletons. This was the first time we’d tried this, so I assembled a panel of what I thought were safe hands and asked this question to one of the safe pair of hands accordingly. We’ll call him Richard, mostly because that was his name. Much to my surprise, what I got back was advocacy of singletons!

I discovered a long time ago that understanding context is one of the most important parts of life, especially when it comes to understanding why people say and believe the things that they do. I think back to a memory from when I was a child, of a piece of film where you see a punk barging an old man out of the way; and then you see it again from another angle and realise he’s saving the old man from a heavy falling object.

When the panel was asked: “Are singletons evil or just misunderstood?” I read out something I had prepared previously, to give a little more context, explaining that Singletons lead to tight coupling; can be difficult to instantiate in threaded environments; and that it was difficult to know when to destroy them. Richard picked up on this and asked me to read it again, which I did.

Richard explained that all of these issues, and more, can be mitigated with the use of modern techniques such as dependency injection [DependencyInjection]. Dependency injection is a method of coupling objects at runtime, rather than at compile time.  As Kevlin Henney pointed out to me dependency injection is a classic, although sometimes verbose and poorly cohesive, application of parameterise from above.

For example, an option pricing engine may get its instrument prices from a number of different sources. You might have one class that retrieves Reuters’ prices and another that retrieves Bloomberg’s prices. They share a common interface, so, the pricing class doesn’t need to know which it is using; it just needs to know about the interface. In traditional systems, determining which object to use at runtime would be done within the code.

In recent years Inversion of Control (IoC) [IoC] containers have become popular.  The main concept behind IoC is, that, the responsibility for creating objects is taken away (inverted!) from the developer and the code; and delegated to a library which constructs objects on demand.  The IoC container knows about (through its configuration) dependency chains and supplies objects with appropriate dependencies for the entire object graph being created through dependency injection. IoC containers do more than construct objects however, they manage the entire object lifecycle and clean up and destroy out of scope objects.

So, in our example, when the IoC container creates the pricing object it will see that it has a dependency on an instrument object and look at its configuration to see which instruments object it needs to create and injects into the pricing object using dependency injection.

Creating an instruments object for Reuters or Bloomberg might be an expensive and time consuming operation and it makes sense to only do it once and then inject it into other objects that need to use it. Enter the managed singleton! One of the configuration parameters you can give to an object managed by an IoC container is: singleton. This means that the container will only ever create one instance of that object.  This doesn’t of course stop someone creating another instance of the object outside of the IoC container, so it doesn’t enforce a single instance programmatically, as with the Gang of Four singleton. However, it does remove tight coupling between the singletons and their dependants.

IoC containers can usually create singletons in a thread-safe manner, which is not the same as making the singletons thread safe. The IoC also container manages the lifetime of the singletons - in fact using an IoC container seems like a pretty good solution to many of the issues associated with singletons.
Richard had changed my mind, so I decided to write about it as I felt this was quite profound, for me at least. However, now that I have written about it I am no longer convinced. I started off describing Gang of Four singletons and then how I favoured parameterise from above.

Managed singletons, as I hope I’ve clearly explained, are very different. They don’t ensure a class has only one instance and do they really provide a global access point? Ironically, in at least one IoC container implementation I know of the container itself is a Gang of Four singleton and has a static access method that must be used if you want to use an object managed by the container outside of the container. Dependency injection too is a form of ‘parameterise from above’.

Maybe common thinking has shifted from considering Gang of Four singletons alone and now managed singletons (or similar) are considered singletons too. Personally, I don’t buy this. Singletons are still dangerous and should be handled with care. Remember, always parameterise from above.

Acknowledgements

Thanks to Geraint Williams, Kevlin Henney and Richard Featherstone for their contributions to the article.

 [GangOfFour] Design patterns: elements of reusable object-oriented software by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. ISBN-13: 978-0201633610

[SingletonPattern] http://en.wikipedia.org/wiki/Singleton_pattern

[ACCU] ACCU: http://www.accu.org/

[PfA] Paramaterise from Above: http://accu.org/index.php/journals/1411, http://accu.org/index.php/journals/1327

[NorfolkDevelopers] Norfolk Developers: http://norfolkdevelopers.com

[DependencyInjection] Dependency Injection: http://martinfowler.com/articles/injection.html

[IoC] Inversion of Control: http://martinfowler.com/articles/injection.html


1 comment:

  1. Gang of Four was 1994 in the Smalltalk and C++ object-oriented world of the time. Many of the patterns are as valid now as then, many are not. Someone needs to write the replacement book to provide Java, Kotlin, Ceylon, Scala, Rust, C++, D, Go, Python, Ruby, Groovy, Clojure, etc. contexts.

    ReplyDelete