What is Singleton?
Singleton is a creational design pattern that ensures that a class has only one instance and also provides a global point of access to it.
The main idea of this pattern is to make a class itself responsible for keeping track of its sole instance. Singleton is considered one of the simplest design patterns but it is also an easy one to get wrong if you are not careful.
Class diagram and basic structure
A general approach to the implementation of Singleton is represented in the class diagram below:
A general approach to the implementation of Singleton is represented in the class diagram below:
- Singleton class contains the static property instance which is a reference to the class instance itself (this relationship is represented as an association link from the class Singleton to itself);
- This instance is only accessible through the static method getInstance();
- Class constructor is marked as private (it could be protected in other implementations) to ensure that the class could not be instantiated from outside the class.
Singleton could be used in cases where creating the instance of a class is expensive e.g. instantiating a class requires loading a lot of data from external sources. Also, the pattern helps when you need to access the same object over and over again across your code . Singleton could also be used when some kind of caching layer is needed — the singleton class could check and manage the cache on the instance request.
General thoughts and dangers
- When designing a Singleton, lazy construction should be considered — class instance should only be created when it is first needed;
- In general, the Singleton class should not require parameters for its construction. If your class design requires a parameter, it could lead to the creation of a somehow different object based on that parameter — could this class still be called a Singleton, then? Some resources state that this is a valid approach, but I have a different opinion;
- Thread safety — you should be aware of Singletons in multi-threaded applications. If they hold some kind of mutable data, it could lead to unexpected results, so the synchronization mechanism should be considered.
Since we are talking about the Dart programming language in this series, you should know that Dart is a single-threaded programming language and its code runs in a little isolated space on the machine, called isolate. Hence, you should not worry about the thread-safety when implementing Singletons in Dart as long as you do not create a new separate isolate from the code by yourself. If you are not familiar with this topic, I strongly recommend you watch this video about isolates and event loops in Dart and Flutter.
- In some cases, the Singleton design pattern is considered an anti-pattern. That is because it violates one (actually, more than one, but this example, in my opinion, is the best one) of the SOLID principles — the single responsibility principle. In addition to the main responsibility of the Singleton class, it should also manage its instance lifetime which is a separate concern. Also, the use of Singletons makes it difficult to unit test the code since it is not possible to mock a Singleton unless you provide some kind of interface that serves as its type.
Implementation
We will use the Singleton design pattern to save our Singleton example’s state in the Flutter Design Patterns application. To make it more straightforward, the state saves only a single text property. Example’s state itself is implemented in three different ways:
- Using a Singleton design pattern which is implemented by definition;
- Using a Singleton design pattern which is implemented using the Dart language capabilities;
- Without using a Singleton at all.
ExampleStateBase
Since the example’s state is implemented in several different ways, its abstraction was created in order to reuse it in all of the implementations. Hence, class ExampleStateBase provides this abstracted state:
example_state_base.dart
As already mentioned, the example’s state consists only of a single String property stateText and its initial value initialText. Properties stateText ant initialText are marked as protected — it is needed to make these properties accessible only for those classes which extend the ExampleStateBase class. However, Dart does not support the protected visibility in the same way as some of you could expect it to be coming from the other OOP language’s background such as C# or Java — we can only annotate these properties as protected but it is more as a reminder for the developer not to use them from outside of the class scope (Visual Studio Code editor even shows a warning in this case). Also, ExampleStateBase provides methods to operate the stateText.
Singleton’s implementation by definition
In the class diagram below, concrete classes of the Flutter Design Patterns application are represented which implement the Singleton design pattern by definition.
- ExampleStateByDefinition extends the ExampleStateBase class to obtain access to the state (in this case, stateText and initialText) and its methods.
- ExampleStateByDefinition implements the Singleton design pattern and handles the instance creation. The instance is only accessible through the static method getState().
Code of the ExampleStateByDefinition: