Let’s start off the design pattern series with one of the simplest and most widely used design patterns: The Singleton Pattern.
Singletons are a creational design pattern that ensures that only one instance of a type exists and that all parts of the application at the ‘country level’ can communicate with it directly.
As a real-world analogy, take the government of a country or its president. You see, in a given country, there can be only one official government or one official president, regardless of the individuals who form this government or the person currently serving as the president of the country. So, the title ‘the government or the president of CountryX’ is a global identifier, a global point of access used to represent or reference the president or the individuals in charge of that country.
Singletons represent exactly the same idea, just like global variables they let you access your object from anywhere in your application but on top of that, they encapsulate the attributes of this object in one single class and guarantee that only one instance of this class will be available at any point in time.
but why would anyone want to control how many instances a class has? Well, the Singleton pattern solves two problems at the same time number one ensures that a class has just a single instance.
why would anyone want to control how many instances a class has the most common reason for this is to control access to some shared resources, for example, a database or a file number two provides a global access point to that instance do you remember those global variables that we use to store some essential objects just like a global variable the Singleton pattern lets you access some object from anywhere in the program however it also protects that instance from being overwritten by other code.
let’s look at this by simple example,
class User {
final int age;
final String name;
User({
this.name,
this.age
});
static User _instance;
static User getInstance({name, age}) {
if(_instance == null) {
_instance = User(name: name, age: age);
return _instance;
}
return _instance;
}
}
print(User.getInstance(name: "Shirsh", age: 29).age);
print(User.getInstance()); // {name: "Shirsh": age 29}
as we saw in this example we can able to change user name and age directly by Singleton class,also we able to set and get this detail anyware without creating again and again instance.
so next thing is how many ways we can able to create Singleton in flutter, like,
Factory constructor
class SingletonFactory {
SingletonFactory._privateConstructor();
static final SingletonFactory _instance = SingletonFactory._privateConstructor();
factory SingletonFactory() {
return _instance;
}
}
Static field with getter
class SingletonStaticFG {
SingletonStaticFG._privateConstructor();
static final SingletonStaticFG _instance = SingletonStaticFG._privateConstructor();
static SingletonStaticFG get instance => _instance;
}
Static field
class SingletonStaticField {
SingletonStaticField._privateConstructor();
static final SingletonStaticField instance = SingletonStaticField._privateConstructor();
}
and all these three you can initilize like this way
SingletonFactory field = SingletonFactory();
SingletonStaticFG fgField = SingletonStaticFG.instance;
SingletonStaticField sField = SingletonStaticField.instance;
basically they are generally used in the packages part so you can see when we used Firebase authentication it was used there so it’s used when you’re using you know some plugin and you want your package users to have only one instance available throughout their code base.
so now you might wonder what are the advantages and disadvantages of you using Singleton design pattern,let’s look into 2 common advantages and disadvantages between them,
Benefit: Less memory consumption
A significant benefit of implementing the singleton design pattern is its ability to minimize memory consumption in your application. By creating a single instance of a class, you can eliminate the need for allocating and deallocating memory for multiple objects of identical type. This can also enhance cache efficiency and reduce the burden of garbage collection. For instance, if you have a singleton class that holds configuration data, you can effortlessly access it from any section of your application without having to create duplicate copies or pass references.
Benefit: Easier access and control
The singleton pattern offers an additional advantage of streamlining the way a shared resource is accessed and controlled. By providing a sole entry point, it guarantees that all elements of your application utilize the identical instance of the resource and that it is appropriately initialized and disposed of. It also permits the implementation of synchronization mechanisms or policies to avert concurrency problems or data inconsistencies. To illustrate, in the case of a singleton class governing a database connection, it guarantees that only one connection is open simultaneously and that it is shut down when not in use.
Drawback: Scalability and testability are reduced
Singletons have the disadvantage of reducing your application’s scalability and testability. The creation of a global state introduces dependencies and coupling between your application’s components, making modifications and extensions more difficult. The singleton instance also limits your application’s flexibility and configuration, since it cannot be easily replaced or mocked for different scenarios or environments. It may be impossible to change the logging level or destination of a singleton class without affecting the whole application, for instance.
Drawback: Risk and complexity are increased
A singleton pattern can also increase the complexity and risk of your application. In order to implement the singleton pattern, you must ensure thread safety, lazy initialization, serialization, and reflection. If your singleton class is not designed and implemented carefully, it can become a source of bugs, memory leaks, or performance bottlenecks. When you have a singleton class that caches some data, it may be necessary to deal with issues such as invalidation, expiration, and synchronization.
Every design pattern has its advantages and disadvantages, but the most important factor is if it meets your requirements better than others. In addition, the information in this article was gathered from a variety of websites and research, so if you find any errors or misdirected links, please let me know.