I woke up this morning to a discussion on Twitter over how different implementations of the Singleton pattern compare. This is like comparing your Herpes: no matter whose is better or more efficient, you still have unsightly blisters.
Overview: wtf is a Singleton?
Singleton is one of the software design patterns originally collected in the famous Design Patterns: Elements of Reusable Object-Oriented Software by the “Gang of Four”. They describe the intent of the pattern thus:
Ensure a class only has one instance, and provide a global point of access to it.
The other useful source of design pattern information is Cocoa Design Patterns by Erik Buck and Don Yacktman. In addition to giving examples of Singleton found in the Foundation and AppKit frameworks, this book shows how to implement a Singleton class in Objective-C. The key features are:
- A single access point to retrieve the single instance.
- Initialization of the single instance.
- Protection against accidentally creating another instance or deleting the single instance.
When would you use that?
Well here’s the thing: I won’t say that I “never would” use Singleton, but I will certainly say that it isn’t the most reached-for tool in my belt.
The usual reason is “this class models something of which there is only one thing”. This is the most absurd thing you’ll ever hear. There’s only one print spooler, so surely it must be a singleton. Right, that only works right up until the point where you need two print spoolers. When do you need a second print spooler? I’ll come onto that in the next section.
Similarly, just because there’s one filesystem, doesn’t necessarily mean that you only need one filesystem object. It certainly doesn’t mean you need to enforce there’s only one filesystem object.
Go on then, wise guy, when do you need the second singleton?
When you test the code that uses the first one. You don’t want your unit tests to talk to the real filesystem, or the real database, or the real print spool. You want your unit tests to use a Mock Object, which means they need the second, fake, instance of your filesystem object or whatever.
That’s the real problem with Singleton: often, in your app, it makes sense to provide a shared instance of an object (particularly one that has state relevant to the entire app). However, that’s not the same as requiring that no-one ever create another instance of the object.
So how would you do it, then?
Let’s assume I have a need for an application-wide Framistan instance. I have a couple of options:
- Create the usual +[Framistan sharedFramistan] class method, as Singleton implementers would, but not all that refcount-avoiding cruft that normally goes with it.
- Create the method -[[NSApp delegate] sharedFramistan]. After all, I have a need for an application-wide instance, and that’s where stuff associated with the application lives.
The option I go for depends on whether I think a process needs a shared instance of the class, or whether I think this app does. Usually, the latter option gets implemented first, and I change it later when I come to write the second app that uses the same class.
Either way, when I need to use the shared instance, I use it like this:
[someFrobnicator abdjulateWithFramistan: [Framistan sharedFramistan]];
Passing the shared instance in means that I still get to pass in other instances, for example in a test I can do [testFrobnicator abdjulateWithFramistan: mockFramistan];.
I don’t like that. Does anyone do something different?
Yes, some people solve Singleton…with another Singleton. The mind boggles. Anyway, what these people do is to create a FramistanFacade Singleton whose job is to manage access to the Framistan Singleton. Now your real Framistan can be an honest Singleton class, but clients talk to the (also-Singleton) FramistanFacade class, which decides what instance of what class it wants to talk to itself.
In Objective-C, the FramistanFacade can use message forwarding to act as a Proxy object, hiding the interaction between Façade and real object. Of course, now that you’re managing the real instance behind the Façade, the real class doesn’t need to be a Singleton because a different class is already managing how its instances are used.
The debates over how best to implement Singleton in Objective-C are redundant because the Singleton pattern is never what you need. Often you do need a shared instance of a class, but enforcing that no other instance ever get used it detrimental to testing.
When you do need a shared instance of a class in your app, ensure that you do not close the door to using alternate instances. The need comes up more often than you might expect.