In this article we will explore the controversial topic of singletons. For some, this singleton pattern an anti-pattern to be avoided at all costs, for others they are just a way of handling situations where we want to restrict the instantiation of a particular class to one instance where we would want to be able to coordinate usage across the system (Ref#: C), or in other words they “ensure a class only has one instance, and provide a global point of access to it” (Ref#: GOF book).
Singletons became a famous design pattern, and in many cases people learn’t about them from the book “Design Patterns: Elements of Reusable Object-Oriented Software” written by the so called Gang of Four (GoF for short). This is a book which has frequently been a required text for undergraduate Computer Science degree students.
Disambiguation
To differentiate a Singleton from other things: A Singleton is a very specific variety of single instance, and it will typically have the following properties
- It is accessible via a global, static instance field / variable
- It’s created either on program initialization, or upon first access
- It will not have a public constructor so we should not be able to instantiate a singleton directly
- It’s never explicitly freed but rather it will be implicitly freed on program termination.
Source – Ref#: ZB
The Controversy
A 2017 stack overflow post linked here illustrates the controversy. In the following article we will explore the two main positions which are articulated in this post and elsewhere online.
What do Singletons Typically Look Like
In the Swift programming language for example, which is now widely used in iOS development of iPhone apps, we frequently see examples of singletons created like the example below, that is using static let statement, and they may or may not have a private initializer to control things better:
SOURCE: Ref#: Y
In Java we might have typically have seen something like this (in this case it attempts to deal with the threading issues we will hear about below):
SOURCE – Ref#: Z
How Does Kotlin Do Singletons?
The Kotlin programming language uses singletons in quite a different manner (Ref#: ZD). In this language a singleton is created by declaring an object
.
object SomeSingleton
“Contrary to a class
, an object
can’t have any constructor [which can be invoked directly], but init
blocks are allowed if some initialization code is needed”(Ref#: ZD). This method of using singletons is apparently considered the best way of using singletons in a language which works with JVM due to insuring thread-safe usage of the singleton which need to resort to some locking algorithm.
The Argument for Using Singletons
The benefits of using singletons can include that they can be used to solve the Resource Contention problem (also known as shared resource encapsulation) – that is where you may have some resource that can only have a single instance, and where you also need to be able to actively manage that single instance then you will arguably benefit from employing a singleton.
Furthermore, they may also solve the Real-World Object problem, successfully representing objects which represent real world resources (Ref#: W), so for example if our phone is using bluetooth we may need some shared manager in our software code for that device with this function being supported by a specific hardware component of the device.
It has further been argued that “People think Singleton is evil because they are using it for globals. It is because of this confusion that Singleton is looked down upon”. It is argued that people “confuse Singletons and globals” but if they are used for their intended purpose they can provide strong benefits.
Returning of shared font sizes and colors in an app is something that developers Alex and Andrew have argued about in their “Inside iOS Dev” podcast to maybe be another case where a singleton could be reasonably and justifiably used (Ref#: ZC), being a simple use case which again could alternatively be accomplished with the use of global static functions.
The Argument Against Using Singletons
“By using singletons, you almost always sacrifice transparency for convenience”(Ref#: Q).
Some most obvious potential problems that people have highlighted with Singletons are:
Singletons can make your code difficult or impossible to test, they can cause a high degree of coupling (a lack of loose coupling) and thus lead to entanglement of your code base, or what we tend to call spaghetti code. This in turn can make reusing your code in our contexts also quite difficult.
There is also an issue with an inability to use abstract or interface classes. We can subclass a Singleton but have to pre-assume that our requirement for what we want from the singleton wont change, otherwise we’d have a heavy re-factoring job on our hands.
There is most definitely a difficulty in implementing unit tests with Singletons (this is because we can’t fake/mock the Singleton in our unit tests without switching to use Dependency Injection)
We can also see possible issues with Threading, become the singleton is really difficult to parallelize, and indeed in the case of it having mutable state, it will require us to potentially have to use extensive locking to avoid basically timing issues with state of our Singleton.
Let look as some of these point in a bit more detail:
Leaking to the Global Namespace and the Global State Problem
Some say that singletons can be mis-used as a form of global (they can be a disguises for a global with mutable state, with using globals being universally recognized as “A Bad Idea”); they say that there is a danger of singletons being just globals by a different name if misused (Ref#: N). It has further been argued that “they introduce global state into a program, allowing anyone to access them at anytime (ignoring scope). Even worse, singletons are one of the most overused design patterns today, meaning that many people introduce this possibly detrimental global state in instances where it isn’t even necessary” (Ref#: T).
Having Global State means, we could call the same constructor on two different occasions, passing in no parameters, and yet yield two different return values(Ref#: B). The problem with Global State is that it is not explicit (it’s hidden) it could drive us crazy as we do essentially the same thing / operations and get different outcomes (as we will point out below this means our unit tests can’t deliver consistent outcomes, and we will get order effects based on the order in which we run the unit test, which is clearly bad, and we can’t run our unit test in parallel with Global State).
Some argue that the “Singleton Design Pattern … has the wrong focus for most purposes: it focuses on object identity, rather than on object state and behavior” . It is furthermore considered as a so-called anti-pattern by some, who feel that it is over used, and indeed that this can introduce unnecessary limitations in situations where a sole instance of a class is not actually needed (Ref#: X).
By having the singleton globally accessible we don’t know what is updating it at any time that our class that we’re currently working on is accessing it. We might suspect that this could, in the end, lead to what we call spaghetti code which we definitely want to avoid in our code.
Singletons Make Unit Testing Difficult
Since singletons can cause your code of your classes to be tightly coupled to the singleton, it can make unit testing more challenging. Like Paul Hudson says, “it can be deeply annoying trying to write tests when singletons are involved”(Ref#: N).
“One of the unique abilities of a singleton is that it can be accessed anywhere though it’s globally available static method (i.e. getInstance()), allowing programmers to use it inside of a method without having to pass it in expressly through parameters. While this may seem easier to the programmer, relying on this static instance means that the signatures of methods no longer show their dependencies, because the method could pull a singleton “out of thin air.” This means that users need knowledge of the inner workings of code to properly use it, making it more difficult to use and test”(Ref#: T).
Furthermore, as Jesse Squires points out, when you make use of these and your own singletons, it increases the amount of work it will take to make changes to your app’s code (that is it can make refactoring your code a lot more difficult) and additionally “makes it difficult or impossible to test your code, because there’s no way to change or mock those singletons from outside of the classes in which they are used” (Ref#: O)
Expanding on Jesse Squires’ point: An issue with singletons is that they create hidden dependencies: essentially due to singletons being readily available to access throughout your code base, they can often end up being overused. In addition, because we can’t easily track references to the singleton it can then become difficult to track how the singleton is being used in our app, and then if two (or more) parts of the app are using the singleton we could get order effects and other strange bugs occurring.
Concurrency Issues can cause problems when using singletons. This can be cause by Not Applying Concurrency Protections, not Applying Concurrency Protections Consistently (Ref#: X). This means if we don’t insure we’re always using the same thread for accessing (reading or writing) from our singleton we will get problems.
So What Should we Use Instead – The Use of Dependency Injection
Having said all of these things about the downsides of singletons, what do the critics of singletons argue we should use instead?
Well Dependency Injection is the typically suggested alternate we see proposed on within the stack overflow world.
The suggestion is “that you should design your classes and functions such that all inputs are explicit” (Ref#: ZA). One key advantage of this approach is that “Dependency injection enforces the order of initialization at compile time” meaning that we are then able to unit test each part of a process in isolation my mocking the injected objects (Ref#: B).
So we may want to inject our Manger
objects in something like a view controller for example, and we can indeed we may then in our unit testing create a Manger
object passing it into something else for a nice discrete test without order effects.
So for example we will inject our database instead of just referring to a database singleton directly:
We can use Swift’s facility for default parameter values when we really only want to change what we’re injecting at the point when we want to unit test our code. We can use the default parameters a bit like this:
Source – Ref#: ZA
The advantage of the above referenced approach is that you can continue to use dependencies in almost exactly the same way, but with the ability to mock dependencies when required for unit testing.
Paul Hudson and others highlight an approach which uses a protocol with an extension and makes types which use it conform to such a protocol (Ref#: N, Ref#: V).
Such an approach as the above could also look like this:
SOURCE: Ref#: I
Conclusion
The singleton is an oft debated topic in the field. They have classically been widely used by certain companies in their code-bases (i.e. Apple’s Cocoa).
On stack overflow we see a pervasive opinion that singletons are overused and close to anti-pattern and have even been described as “Evil” 🙂 We see that the proposed alternatives typically involve using a form of Dependency Injection.
However, we have seen that Singletons actually (in some cases) may solve certain important problems, and that therefore the key when optimizing code might be simply to reduce these to a absolute minimum (and incorporate them more intelligently via dependency injection). We ought to be asking ourselves as programmers:
- Will every place we use our singleton make use of it in exactly the same way?
- Will we really only ever need one instance?
- Should we use D.I. such that the clients classes are essentially more aware (as it were) of which dependencies they’re using more explicitly?
So if our singleton is really a global with mutable state in disguise then we really should think about ditching it and refactoring! We should indeed then be using alternatives where we can, but occasionally using singletons to solve the Resource Contention Problem, and the Real-World Object problem can, in my opinion, actually make sense and be OK to do in some cases, but again ideally using via D.I. only to allow mocking and meaning that we can actually make it possible to make a second (or more) instances for the purpose of testing.
Last Updated: Feb 16th 2019
References
A: https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons
B: https://www.youtube.com/watch?v=-FRm3VPhseI
C: https://www.youtube.com/watch?v=hUE_j6q0LTQ
D: https://www.youtube.com/watch?v=wEhu57pih5w
E: https://www.youtube.com/watch?v=-FRm3VPhseI
F: http://staff.cs.utu.fi/~jounsmed/doos_06/material/SingletonAndMonostate.pdf
G: https://www.codeproject.com/Tips/692285/Monostate-pattern
H: https://www.swiftbysundell.com/posts/avoiding-singletons-in-swift
I: https://medium.com/@hartwellalex/protocol-extensions-and-shared-dependency-injection-in-swift-an-alternative-to-singletons-68934dee6998
J: https://www.objc.io/issues/13-architecture/singletons/
K: https://accu.org/index.php/journals/2085
L: https://blogs.msdn.microsoft.com/scottdensmore/2004/05/25/why-singletons-are-evil/
M: https://medium.freecodecamp.org/singleton-design-pattern-pros-and-cons-e10f98e23d63
N: https://www.hackingwithswift.com/articles/88/how-to-implement-singletons-in-swift-the-smart-way
O: https://www.jessesquires.com/blog/refactoring-singletons-in-swift/
P: http://puredanger.github.io/tech.puredanger.com/2007/07/03/pattern-hate-singleton/
Q: https://cocoacasts.com/are-singletons-bad/
R: https://thatthinginswift.com/singletons/
S: https://sites.google.com/site/steveyegge2/singleton-considered-stupid
T: https://code.google.com/archive/p/google-singleton-detector/wikis/WhySingletonsAreControversial.wiki
U: http://wiki.c2.com/?SingletonPattern
V: https://www.udemy.com/design-patterns-swift/
W: Freeman, A (). Pro Design Patterns in Swift. APress.
X: https://www.youtube.com/watch?v=NZaXM67fxbs
Y: https://cocoacasts.com/what-is-a-singleton-and-how-to-create-one-in-swift
Z: https://antonioleiva.com/objects-kotlin/
ZA: https://www.jessesquires.com/blog/refactoring-singletons-in-swift/
ZB: https://softwareengineering.stackexchange.com/questions/40373/so-singletons-are-bad-then-what
ZC: http://insideiosdev.com/newyear2019-and-singletons
ZD: https://medium.com/@BladeCoder/kotlin-singletons-with-argument-194ef06edd9e
Comments