Saturday, August 30, 2014

Singletons Reconsidered

TL;DR

Don't make it a global, use it only for stateful resources, and don't use them if you can't implement them properly due to language or ability. Add management controls to the interface so that you can control the behavior of the Singleton in cases like testing, debugging or resetting.

Introduction

 Everyone by now knows about all arguments.

Testability

The typical complaint is that singletons are global and that makes them hard to test and be in tests.  In most languages we can address those issues directly.

  1. Don't make the Singleton global, make it scoped to the Singleton class or module.
  2. Support management controls like a reset or clear method.
There is no reason to make a Singleton global. You should be able to import the class that will return the Singleton. Ideally you make the Singleton truly instantiate with the first constructor call. Any other constructor call would just be returning the already constructed object.  For all usages it becomes just another constructor call that happens to return the same object.

The Singleton should persist state, which does make it harder to test. However, if you add management controls then the Singleton is not a problem for testing.  If you add a reset or destroy to the Singleton class is completely testable.

Hidden Dependencies

If it's no longer a global that means you have an explicit import our include.  It's inclusion is no longer assumed, and as a result you know if a given module uses the Singleton because it has the import.  The dependencies are no longer hidden, they are explicit and clear.

Violates the Single Responsibility Principle

No it doesn't.  At it's core SRP refers to cohesion and coupling.  Two things that aren't cohesive should not be coupled together because changes in one should not impact the other.  However if they are in the same class you have coupled them together so when either responsibility changes the entire class has to change as well.  This is tight coupling.

This has nothing to do with an object being a Singleton unless it is somehow exporting it's ability to be a Singleton (like a meta class, mixin or template class might).  Being a Singleton is a property of the class, that doesn't mean the behavior is primary, ie. The intent of the class is not to provide Singleton behavior out to other objects. Since the Singleton behavior is encapsulated and not exposed SRP remains intact.

Doesn't Work Right in Language X

Yeah, well that's self explanatory.  Don't use language X or if you have to use language X then don't use Singletons. 

Threading

Now that is a real argument.  Yes Singletons can suck in a threaded application unless the Singleton has  semaphores or mutexes to create the appropriate critical sections.  Yes it's hard to get right, and you may not know you didn't get it right until that weird bug happens in production. HOWEVER, that is an ongoing risk of threaded programming regardless of Singleton usage.  Singletons might make it a little more likely you screw it up, but it's not going to be in some novel way.

This risk is also completely mitigated in the case of a read only Singleton, such as a Config object.

Singletons Done Right IMO

Okay, so I'm not a hotshot programmer.  I consider myself a decent bordering on good programmer.  With all those caveats upfront,  here is how I do Singletons.

Override Instantiation to return the same instance always, or the same instance given the constructor arguments as a unique key.

Make the actual instantiation of the Singleton lazy. So it just does the right thing regardless of actually creating the object the first time underneath the covers, or simply returning the same object that already exists.

Always provide an explicit reset or destroy for the Singleton to facilitate testing.


No comments:

Post a Comment