Skip to content

Instantly share code, notes, and snippets.

@paulresdat
Last active November 8, 2021 17:03
Show Gist options
  • Save paulresdat/866d654174928fdf4d02462ab8c45e3d to your computer and use it in GitHub Desktop.
Save paulresdat/866d654174928fdf4d02462ab8c45e3d to your computer and use it in GitHub Desktop.
C# style service collection in dart
enum ServiceMetaDataType {
singleton,
transient,
}
class ServiceMetaData<T> {
late dynamic _instanceFunc;
T? _instance;
late final List<String> _dependencies;
late final ServiceMetaDataType _serviceType;
late final Map<String, ServiceMetaData> _collectionReference;
ServiceMetaData(
dynamic instanceFunc,
this._dependencies,
this._serviceType,
this._collectionReference) {
_instanceFunc = instanceFunc;
}
List<dynamic> _getDependencies() {
var deps = <dynamic>[];
for (var dep in _dependencies) {
// recursively building dependencies
deps.add(_collectionReference[dep]!.instance);
}
return deps;
}
ServiceMetaDataType get serviceType => _serviceType;
T get instance {
if (_serviceType == ServiceMetaDataType.singleton) {
if (_instance == null) {
if (_dependencies.isNotEmpty) {
var deps = _getDependencies();
_instance = Function.apply(_instanceFunc, deps);
}
else {
_instance = _instanceFunc();
}
}
return _instance as T;
}
else if (_serviceType == ServiceMetaDataType.transient) {
if (_dependencies.isNotEmpty) {
return Function.apply(_instanceFunc, _getDependencies());
}
else {
return _instanceFunc();
}
}
throw UnimplementedError();
}
}
class ServiceProvider {
late Map<String, ServiceMetaData> _serviceCollection;
ServiceProvider(this._serviceCollection);
T getService<T>() {
return _serviceCollection[T.toString()]!.instance as T;
}
}
class ServiceCollection {
ServiceCollection();
final Map<String, ServiceMetaData> _services = <String, ServiceMetaData>{};
void addSingleton<T>(Function registration, {List<dynamic>? injectedTypes}) {
if (injectedTypes != null) {
var dependencies = _getDeps(injectedTypes);
_registerDependency<T>(registration, dependencies, ServiceMetaDataType.singleton, _services);
}
else {
_registerDependency<T>(registration, <String>[], ServiceMetaDataType.singleton, _services);
}
}
List<String> _getDeps(List<dynamic> injectedTypes) {
var dependencies = <String>[];
for (var dep in injectedTypes) {
dependencies.add(dep.toString());
}
return dependencies;
}
void _registerDependency<T>(dynamic reg, List<String> deps, ServiceMetaDataType serviceType, Map<String, ServiceMetaData> _services) {
_services[T.toString()] = ServiceMetaData(reg, deps, serviceType, _services);
}
void addTransient<T>(Function registration, {List<dynamic>? injectedTypes}) {
if (injectedTypes != null) {
_registerDependency<T>(registration, _getDeps(injectedTypes), ServiceMetaDataType.transient, _services);
}
else {
_registerDependency<T>(registration, <String>[], ServiceMetaDataType.transient, _services);
}
}
ServiceProvider buildServiceProvider() {
return ServiceProvider(_services);
}
}
// This is a C# kind of way of registering services using a service collection and service provider.
// This service collection works without mirrors.
// The primary problem with Flutter and mirrors, is that it bloats the application and has performance issues.
// It's not recommended to use Flutter with mirrors. This way of registering services and their dependencies
// has a somewhat manual approach but allows for injection into your application. It is recommended
// that you inject your objects from the top most layer in. Do not pass the service provider around as that
// is an anti-pattern.
abstract class IDatabase { }
class SqlDriver implements IDatabase { }
abstract class ISql { }
class SqlManager implements ISql {
final IDatabase _db;
SqlManager(this._db);
}
var sc = ServiceCollection();
sc.addTransient<IDatabase>(() => SqlDriver());
sc.addSingleton<ISql>((IDatabase db) => SqlManager(db), injectedTypes: [IDatabase]);
var sp = sc.buildServiceProvider();
// will have the interface ISql, but be the concrete object SqlManager
var sql = sp.getService<ISql>();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment