the perks and perils of the singleton design pattern
TRANSCRIPT
Singleton The Perks and Perils of the Singleton Design Pattern
Motivation
You have that one, and exactly one object:● Database connection● Hardware interface● …
and you have to make sure all parts of the application use that one and only object
Solution
Head up to that class, and:
● Restrict the class to create only one object
● Provide global access to it
Ref: [1]
Example Single Instance
Global AccessUse it anywhere
View 1
View 2
Users Store
Some object
Example - Diagram
Example - Even more!
View 1
View 2
View 3
Users Store
Departments Store
Singletons
(DB / Backend)
Connection
Techniques
Lazy initialization
Ref: [2]
Techniques 2
Synchronized lazy initialization
Clearer, but lower concurrencyHigher concurrency
Ref: [2]
Techniques - 3
Eager initialization
No need for synchronization
Ref: [2]
Benefits
● State shared globally as required
● Global access; No complications to object access
● The object handles its own creation logic
● Hide details of the Singleton
● Can be used to implement some other patterns as well
○ Abstract Factory, Builder, Prototype …
The endNot really!
Singleton as an anti-pattern
Singleton is popular because:● It was included in GoF Design Patterns
● It’s easy; the easiest in the book
People started a rebellion:● Any design pattern can be abused, except for Singleton
● Valid motive, but ruins everything with its two stepsRef: [3, 10]
Glorified Global Variables
Have a bug? Now go over all those files for a clue!
Mutable state
1This one
caused the bug
2This one found it
Ref: [4]
Glorified Global Variables
When some code uses a global variable, it’s harder to reason about it
Mutable state
1To know what this object / function is
doing
2You also need to
understand what this is
doing
Ref: [4]
Lifecycle
The Singleton controls its own creation and lifecycle● Violation of SRP:
○ Connecting to database is a responsibility○ Creating, opening and closing the connection are other
responsibilities
● Resetting the state is harder and more error-prone than creating a new object
Ref: [3, 5]
Lifecycle
● Now you have to make all Singletons reset to initial state
● You have to wait to reset properly○ Sync data○ Clear cache
Lifecycle
● The user’s signed-in session is shorter than the
application’s lifecycle.
● A global state, if necessary, should only handle states
that last for the entire application’s complete lifecycle
Ref: [5]
Hiding Dependencies
● You don’t know what that Singleton needs○ Singleton.getInstance() doesn’t explain much
Data List Singleton
What you see Data List Singleton
What it actually is
Connection Authenticator
User
Ref: [3]
Hiding Dependencies - Example
● Only works when you run as part of the suite.
● When run in isolation, throws NullPointerException.
Ref: [3]
Hiding Dependencies - Example
● You ask a senior, and he tells you that CreditCardProcessor needs to be handled first
● Still, it throws an exception
Ref: [3]
Hiding Dependencies - Example
● You ask more. Now you know that both Database and OfflineQueue need to be initialized first
● The code worked!
● Now your bank account only has $12.5 because this code charged you for $100
Must be done in the same order!
Ref: [3]
● The code is not stable, if Database gets modified, other classes may fail○ Because you don’t know what depends on Database and how
● The code is not flexible, anything that depends on a Singleton is hard wired to that Singleton
● You can’t unit test a class without the Singleton initialized
● You can’t use mock objects for testing
Hiding Dependencies
Ref: [3]
Encourage Coupling
● Since it’s global, any class anywhere can get it easily● If the developer is thinking hard how to get it in that
class, he would think if that class should get it
Coupling between SomeTransaction and MySQLConnection
Ref: [4]
Encourage Coupling
● Now, you need to use a NoSQL database along with the SQL one
● SomeTransaction should switch to NoSQL database
SomeTransaction
MongoDBConnection
MySQLConnectionTightly coupledRemove the coupling between SomeTransaction and MySQLConnection first
Limiting Instantiation
Are you really sure you won’t need another object of that Singleton class?
nobacks.com
SingletonScreen
We only need one object for that one screen
nobacks.com
So what happens when I plugthat screen to the laptop?
Big deal!
I don’t care if they’re bad or not.
I only use one or two Singletons,what’s going to happen anyway?
Source
That’s the real problem!
This one or two Singletons are strangling all or most of
classes
○ Many classes are tightly coupled to it
○ Many classes can’t be tested properly
○ Change something in that Singleton and all other classes need
to be changed as well
The Root of the Problem
This part is very
legitimate!
This is not
Ref: [9]
Solution
● Pass it a.k.a “Inject it!”
● Get it from somewhere already global
○ That place is responsible for creating it and managing its
lifecycle
○ There are some evils that we can only reduce, but can never
eliminate, like global state
Ref: [7]
Example
A whole bunch of singletons
Ref: [7]
Example
new OfflineQueue(database)
Ref: [7]
Example
● We’re sure that only one object is being used
● Dependencies are declared
● Dependencies are passed
● Each class is testable and mockable
● “Creation” is the factory’s responsibility
● If you ever need to have a new database, you don’t
have to modify available classesRef: [7]
Immutable State Singletons
Immutable state Singletons, enums in Java, objects in Scala, … are okay
But still, be aware of coupling, limiting classes from inheritance, hiding dependencies …
Ref: [9]
References1. c2.com: Singleton2. Wikipedia3. SO: What's so bad about singletons4. gameprogrammingpatterns.com5. Avoiding Singleton Abuse6. programmers.stackexchange: So singletons are bad then what7. Singletons are pathological liars8. Where have all the singletons gone9. Root cause of singletons
10. Singletons considered stupid
Questions?