DESIGN PATTERNS IN JAVA SERIES
Design Patterns in Java/Android — Singleton
Explanations, Caveats, Examples from Android Framework & Java Libraries, Writing our own Singleton
This series on Software Design Patterns will introduce some common design patterns that we use in app development.
- Singleton ← we are here
- Builder
- Factory
- Prototype
- Adapter
- Facade
- Observer
- Command
Here, we will take examples of existing design patterns in Android framework & Java Libraries and also write our own patterns in Java.
Introduction to Design Patterns
Software Design Patterns are time-tested, reusable solutions to recurring software problems. They define a common language that helps our team communicate more efficiently.
Design patterns usually deals with objects. The patterns differ by their complexity, reusability, purpose and scale of application in the system. All patterns an be categorized mainly into 3 categories based on their purpose:
- Creational patterns: How the objects are created.
Eg. Singleton pattern, Builder pattern, Factory pattern - Structural patterns: How the objects are composed (assembling objects into larger structures).
Eg. Adapter pattern, Facade pattern, Decorator pattern - Behavioral patterns: How the objects coordinate (communication and assignment of responsibilities between objects).
Eg. Observer pattern, Strategy pattern, Command pattern
Singleton Design Pattern
The Singleton is the most common design pattern. It is a simple creational design pattern. This pattern ensures a class has one instance at most and the class itself provides a global point of access. If one/many clients request for that class, they get the same instance of the class. This singleton class can instantiate itself, or we can delegate the object creation to a Factory class (another creational pattern, we will learn in future article).
Examples of Singleton from Android Framework & Java Libraries
NOTE: You may skip to “Writing our own Singleton in Java” if you know only Java, but not Android
In a standard Android app, many times we need only one global instance for classes (whether we are using that object directly or passing it to another class). We use Singleton Pattern at the time. Examples of Singleton patterns in Android Framework and Libraries are: SharedPreferences, most Firebase APIs, OkHttpClient, Database connection, Retrofit, Gson, etc.
Android SharedPreferences
A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. For any particular set of preferences, there is a single instance of this class that all clients share.
SharedPreferences
is a Singleton object, it opens the file for shared preferences only when we callgetSharedPreferences
first time. So, even when we get many references ofSharedPreferences
, there is no issue.- As
SharedPreferences
is a Singleton object, if we modify any of its references, the data gets modified for all of them. This happens because there is only one instance of any Singleton class.
Firebase Analytics
Firebase Analytics (closely related to Google Analytics) provides free, unlimited reporting on hundreds of distinct events. The FirebaseAnalytics
(top level of Firebase Analytics) is a singleton that provides methods for logging events and setting user properties.
FirebaseAnalytics.getInstance()
returns a SingletonFirebaseAnalytics
interface. So, even when we get many references ofFirebaseAnalytics
, there is no issue.
OkHttpClient
OkHttp is an HTTP client for Android and Java applications.
OkHttpClient
shown below is a Singleton
Writing our own Singleton in Java
Let us create our own Singleton Pattern now.
The singleton pattern restricts the instantiation of a class to one object. Sometimes we need to have such class. Let’s see various ways of implementing such a class. If we have a good understanding of static class variables and access modifiers, achieving this should not be a difficult task.
1. Basic Lazy/Late Initialization Singleton
Here, we have declared getInstance() static, so we can call it without instantiating the class. When we call getInstance() for the first time, it creates a new singleton object and but after that it simply returns the same object. The Singleton instance is not created till we call getInstance() method. That is why it is called Lazy initialization.
The primary issue in above method is that this design is not thread safe. Two threads calling it simultaneously for the first time can create 2 objects for the Singleton.
2. Synchronized Lazy/Late Initialization Singleton
By using synchronized, we are making sure that only one thread can execute getInstance() at a time. So, the issue in the “design 1” above is resolved. Thus, this design is thread safe.
Only disadvantage in this design is that since synchronized is used every time while creating a singleton object, and it is an expensive process, so it may decrease the performance of our program slightly.
If performance of getInstance() is critical (it is not called huge number of times) in our app, this is a decent solution.
3. Eager/Early Initialization Singleton
We are creating instance of Singleton in static initializer. The Singleton instance is created without the need to call getInstance() method. That is why it is called Early initialization. In this design, the JVM executes static initializer when the class is loaded, so this design is thread safe.
If our Singleton class is light (since class is created in the beginning) or it is used throughout the execution of our app, this is a decent solution.
4. Double Checked Locking Singleton
Here, we have declared the Singleton instance as volatile because we want to ensure that multiple threads offer the instance variable correctly when it is being initialized. We an also use final instead of volatile, that will also serve the purpose.
- final: we can only write to final once and that is visible to other threads
- volatile: any writes to the volatile field will be visible from other threads, so when we write to field, that is visible to other threads that try to read field
The design in above code provides huge improvement by reducing the overhead of calling the synchronized method every time. This design is thread safe and the best among all designs presented here.
-> Running our Singleton Patterns
This code can be used to run all the 4 methods of Singleton given above.
This article is supposed to serve as a beginner’s guide for the above design pattern.
Give a clap if you liked the article or a leave a comment for any suggestion/discussion/dispute. Thank you!