I read these posts:
I'm having a little trouble understanding the difference between the following ways of creating singletons:
1. Factory constructor
class SingletonOne { SingletonOne._privateConstructor(); static final SingletonOne _instance = SingletonOne._privateConstructor(); factory SingletonOne(){ return _instance; } }
2. Static field with getter
class SingletonTwo { SingletonTwo._privateConstructor(); static final SingletonTwo _instance = SingletonTwo._privateConstructor(); static SingletonTwo get instance { return _instance;} }
3. Static field
class SingletonThree { SingletonThree._privateConstructor(); static final SingletonThree instance = SingletonThree._privateConstructor(); }
These are instantiated like this:
SingletonOne one = SingletonOne(); SingletonTwo two = SingletonTwo.instance; SingletonThree three = SingletonThree.instance;
Günter Zöchbauer said about this question:
There is no need to use the factory constructor. The factory constructor was convenient when new was not yet optional because then it
new MyClass()
worked for classes where the constructor returned a new instance every time or where the class returned a cached instance. It was not the callers responsibility to know how and when the object was actually created.
I don't understand how new
being optional now makes the factory constructor unnecessary now. Before you couldn't do something like SingletonTwo
or SingletonThree
above?
Günter Zöchbauer also said:
You can also change
static final DbHelper _db = new DbHelper._constr();
tostatic final DbHelper singleton = new DbHelper._constr();
and remove the singleton getter I suggested in my answer. It depends on your use case. You might not be able to use a field initializer if you need additional config values to create the instance. In your example it would be sufficient though.
What are the use cases for each of the singleton patterns above (SingletonOne, SingletonTwo, and SingletonThree)? It would be helpful to see an example for each. Wouldn't the factory pattern be useful if you wanted to hide the fact that the class was a singleton (as described here)?
There are two forms of singleton design pattern, which are: Early Instantiation: The object creation takes place at the load time. Lazy Instantiation: The object creation is done according to the requirement.
I would argue the most common way to implement a singleton is to use an enum with one instance. That might be a "better" way but definitely not the most common way. In all the projects I have worked on, Singletons are implemented as I have shown above.
There are two types of singleton implementations: eager and lazy initialization.
Well-designed singleton can have only one instance per application. Creating of multiple instances is a mistake in the application design. It might happen in some cases, e.g.: Non thread safe singleton with lazy initialization: several threads are trying to get an instance and creates multiple instances.
As Günter Zöchbauer stated in the comments, each of the three ways you listed of creating singletons are the same. Use your personal preference to choose one.
I'm going to add some additional notes:
SingletonOne
when instantiated looks like any other class. So you could use this one if you want to hide the fact that it is a singleton (and leave the option to make it not be a singleton in the future). You could also pass in arguments in the constructor.SingletonTwo
would allow you to do other work before returning the instance.SingletonThree
is the shortest, and short clean code is desirable in my book, all other things being equal.Since Dart allows root-level variables, a perfectly good, lazy loading Singleton can be had thusly:
final store = _Store(); class _Store { // }
Like your three other examples, this won't work if you need asynchronous construction. Also, like SingletonTwo and SingletonThree you can't pass in any arguments from the calling scope.
For a singleton that needs async construction and arguments I would use something like this:
class StoreService { static StoreService? _instance; StoreService._() {} static Future<StoreService> instance() async { // we can await things here if (_instance == null) { _instance = StoreService._(); } return _instance!; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With