Last updated:
0 purchases
singleton
singleton #
Why create this library #
Singleton is useful pattern, it can help to:
instantiate objects lazily to speed up app loading
instantiate objects on demand, so it reduce memory wasted on unused objects
object is either complicated or expensive to instantiate, but it is used across the app, so having a singleton is economic option
object depends on asynchronous resources, but need to be used in synchronous way. So it has been to created before hand.
But sometimes, singleton could be a hassle to use, because:
Implement lazy pattern every time used is painful and could be avoided.
Singleton are very unfriendly to unit test, as it lives through the test, could cause unexpected test failure.
Singleton depends on asynchronous resource could be complicated to manage.
Singleton library is designed to make those scenario less hassle, enable developer to use Singletons in dart elegantly.
What this library can do #
This library majorly supports 3 different singleton usage:
Lazy Singleton: Type is created lazily and on demand. It behaves similar to Kotlin lazy delegated property.
Eager Singleton: Type is complicated to create, or creation depends on resources only available in some cases. So eagerly creating an instance could be good idea. Kotlin object declaration
Future Singleton: Type depends on async resource to instantiate, but needs to be used in synchronous-enforced environment, such as depends on SharedPreferences's value in Widget build method.
Allow to reset all registered singleton for test, so across test pollution can be mitigated.
This library is designed with Flutter in mind, but it doesn't depends on any flutter specific code, so it can be used anywhere dart works.
Lazy Singleton #
Implement lazy singleton manually #
You might write following code a thousand times:
class MyLazyService {
static MyLazyService _instance;
static MyLazyService get instance {
if (_instance == null) {
_instance = MyLazyService._();
}
return _instance;
}
/// Private constructor
MyLazyService._() {
}
/// do something
void doSomething(){
}
}
MyLazyService.instnace.doSomething();
copied to clipboard
It is working but boring to write, and it has some issues, such as pollutes test.
Lazy singleton with Singleton #
class MyLazyService {
/// Factory method that reuse same instance automatically
factory MyLazyService() => Singleton.lazy(() => MyLazyService._()).instance;
/// Private constructor
MyLazyService._() {}
/// do something
void doSomething() {}
}
MyLazyService().doSomething() // Look like a new instance but it is a singleton.
copied to clipboard
Eager Singleton #
class MyEagerService {
/// Factory method that reuse same instance automatically
factory MyEagerService() => Singleton<MyEagerService>().instance;
final MyApi api;
/// Constructor create and register new instance
MyEagerService.initialize(this.api) {
// Register current instance
Singleton.register(this);
}
/// do something
void doSomething() {}
}
void main() {
final appSettings = getAppSettings();
final httpClient = createHttpClient(appSetting);
final api = createApi(httpClient);
MyEagerService.initialize(api) // Create and register the the singleton
.doSomething(); // Use the instance
}
MyEagerService().doSomething(); // Use the singleton instance
copied to clipboard
Future Singleton #
It could be tricky to deal with the singleton which depends on async resource. Unfortunately in Flutter/Dart, async resources is everywhere.
Here is a close-to-real example how to deal with this case with Singleton library:
Some background types declaration:
class AppSettings {
static Future<AppSettings> loadAppSettings() {
// load app settings from somewhere asynchronously
}
}
class HttpClient {
final AppSettings appSettings;
HttpClient(this.appSettings);
}
copied to clipboard
Type uses Future singleton
class MyFutureService {
/// Factory method that reuse same instance automatically
factory MyFutureService() => Singleton<MyFutureService>().instance;
static Future<MyFutureService> createInstance() async {
final appSettings = await Singleton<AppSettings>().ensuredInstance();
final httpClient = HttpClient(appSettings);
return MyFutureService._(httpClient);
}
final HttpClient httpClient;
MyFutureService._(this.httpClient);
/// Some method
void doSomething() {}
}
copied to clipboard
Register future singleton. Singleton.register understands Future, which resolves Future and registered the value of Future as singleton.
void main() {
// Register AppSettings settings as a future singleton
Singleton.register(AppSettings.loadAppSettings());
// Create and register the the MyService as singleton
Singleton.register(MyFutureService.createInstance());
runApp();
}
copied to clipboard
Use Future Singleton #
Future singleton can be used as other types of singleton,
MyFutureService().doSomething();
copied to clipboard
But as the singleton is parsed from future, singleton is used before the future resolves, an StateError says "ingleton is being used before being resolved" would be thrown.
The error can be omit by ensure the instance execute ensure instance creation at check point convenient:
await Singleton.ensureInstanceFor(MyFutureService);
copied to clipboard
Multiple types can be checked together:
await Singleton.ensureInstanceFor([MyFutureService, AppSettings]);
copied to clipboard
Errors #
If error is thrown by the future, the error won't lost, it is rethron when Singleton.ensureInstanceFor or Singleton.instance is called.
Support Unit Test #
Singleton could cause unexpected test failure due to they lives across the test boundary. Singletons created by Singleton library can be cleared via apis that only visible in tests.
setUp((){
// Reset singleton registry before setup environment to avoid potentially pollution
Singleton.resetAllForTest();
Singleton.register(....) //
});
tearDown(() {
Singleton.resetAllForTest(); // Reset singleton registry to avoid singleton pollution
});
copied to clipboard
Find out what is registered #
Sometimes you might want to check the singleton status for diagnosis purpose. you can achieve it by:
Singleton.debugPrintAll();
copied to clipboard
Or you only cares about a certain types
Singleton.debugPrintAll(MySingleton);
Singleton.debugPrintAll(Singleton<MySingleton>());
Singleton.debugPrintAll([MySingleton, AnotherSingleton]);
Singleton.debugPrintAll([Singleton<MySingleton>(), Singleton<AnotherSingleton>()]);
Singleton.debugPrintAll([Singleton<MySingleton>(), AnotherSingleton]);
copied to clipboard
Remove singleton #
This suppose to be rare, but in some extreme case, if you want to get rid of your singleton. It is possible:
Singleton<MySingleton>().deregister();
copied to clipboard
License #
The MIT License (MIT)
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.