31

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;

Questions

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(); to static 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)?

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393

2 Answers2

21

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.
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
5

Since Dart allows root-level variables, a perfectly good, lazy loading Singleton can be had thusly:

final store = _Store();

class _Store {
//
}

Limitations

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!;
  }
}
Jannie Theunissen
  • 28,256
  • 21
  • 100
  • 127