Purpose
Singleton
(structural pattern) is primarily designed to limit the creation of objects of a given class to one instance and provide global access to it. It owes its origin to C ++
, where it played the role as the successor to the global variable used by the preprocessor. This pattern arouses a lot of controversy - seemingly simple, easy to implement, but badly used is a popular anti-pattern. This happens when its role is reduced to a global variable without considering the use context. Singleton
can be used where there should be only one shared instance of the class.
Limitations
Singleton
is created based on a private constructor that prevents to extend the class. In addition to managing its life cycle, it plays the role of business logic, which violates the principle of SRP
- a single responsibility. What’s more, it breaks the principle of OCP
- open / closed, which says that classes should be open to extension but closed to modifications. Singleton
makes testing difficult because before calling the tests, properly initialization of object must be provided. Due to the architecture of the Android
system Singleton
is not a recommended pattern in most cases. Singleton Mutable
can lose its state when the system needs to release the memory resources. Whereas Singleton Immutable
can often be replaced with Utility
classes (with static methods).
Usage
This pattern is used in accessing SharedPreferences
. Also a popular practice is to implement for objects such as Logger
or data access APIs, eg: Retrofit
.
Implementation
There are many implementations of this pattern. One of the simplest is based on initializing the object through the public getInstance
method only when it is first called. The constructor is invisible from outside the class.
The following listing shows the simplest implementation of Singleton
. However, it causes problems in a multi-threaded environment.
Double checking and locking should be used for Singleton
in multithreaded applications.
The optimal solution is Singleton Holder
, which ensures lazy creation of instances and does not require synchronization.
Example
The application is designed to store and display user data. Access to data should be possible from different places of application. To solve the problem SharedPreferences
(which allows to store application settings) can be used. Read access and write access is provided by the class instance, and these operations are independent of this instance. Therefore, there is probably no need to store multiple SharedPreferences
instances in memory. Access to application settings can be used in many places, therefore the use of the Singleton
pattern seems to be justified. SharedPreferences
implicitly uses the Singleton
pattern. The following listing presents its explicit implementation.
Libraries
The proper use of Singleton
is the use of the Dagger 2
framework, implementing the ‘Injection of dependencies’ pattern.