build method
Builds and returns an instance of type T
from the registered factories,
initializing all types that implements IInitializable.
Factories are ordered in a topological order, ensuring that dependencies are resolved before the instance is created.
T
: The type of the instance to build.
Throws an StateError if no factory is registered for the type T
or
if a cyclic dependency is detected.
Implementation
Future<Dependencies> build() async {
final graph = <String, List<String>>{};
final inDegrees = <String, int>{};
for (final dep in _dependencies) {
final typeName = dep.getTypeName();
graph[typeName] = [];
inDegrees[typeName] = 0;
}
for (final dep in _dependencies) {
final typeName = dep.getTypeName();
for (final dependency in dep.dependsOn) {
final dependencyName = dependency.toString();
if (graph.containsKey(dependencyName) == false) {
throw StateError(
"Dependency '$dependency' not found for '${typeName}'.",
);
}
graph[dependencyName]!.add(typeName);
inDegrees[typeName] = (inDegrees[typeName] ?? 0) + 1;
}
}
final queue = Queue<String>();
final sorted = <Dependency<dynamic>>[];
for (final entry in inDegrees.entries) {
if (entry.value == 0) {
queue.add(entry.key);
}
}
while (queue.isNotEmpty) {
final current = queue.removeFirst();
final currentDep = _dependencies.firstWhere(
(dep) => dep.getTypeName() == current,
);
sorted.add(currentDep);
for (final neighbor in graph[current]!) {
inDegrees[neighbor] = inDegrees[neighbor]! - 1;
if (inDegrees[neighbor] == 0) {
queue.add(neighbor);
}
}
}
if (sorted.length != _dependencies.length) {
throw StateError("Cyclic dependency detected!");
}
for (final dependency in sorted) {
final typeName = dependency.getTypeName();
_logger.info("Instantiating ${typeName}");
final instance = dependency.factory(this);
_instances[typeName] = MapEntry(this, instance);
}
final completers = {
for (final dep in sorted) dep.getTypeName(): Completer<void>(),
};
final noDeps = <Future<void>>[];
final noDepsNames = <String>[];
for (final dep in sorted) {
if (dep.dependsOn.isEmpty) {
final typeName = dep.getTypeName();
final instance = _instances[typeName]!.value;
if (instance is IInitializable) {
noDepsNames.add(typeName);
noDeps.add(instance.initialize());
completers[typeName]!.complete();
}
}
}
if (noDeps.isNotEmpty) {
_logger.fine("Initializing ${noDepsNames.join(", ")}");
await Future.wait(noDeps);
}
Future<void> initializeDependency(String typeName) async {
if (completers[typeName]!.isCompleted) {
return;
}
final instance = _instances[typeName]!.value;
if (instance is IInitializable) {
final dependency = sorted.firstWhere(
(dep) => dep.getTypeName() == typeName,
);
final dependenciesCompleters = dependency.dependsOn
.map((d) => completers[d.toString()]!)
.where((c) => c.isCompleted == false)
.map((c) => c.future);
final dependenciesNames = dependency.dependsOn
.map(
(d) => MapEntry<Type, Completer<void>>(
d,
completers[d.toString()]!,
),
)
.where((c) => c.value.isCompleted == false)
.map((c) => c.key)
.toList();
if (dependenciesCompleters.isNotEmpty) {
_logger.fine("Initializing ${dependenciesNames.join(", ")}");
await Future.wait(dependenciesCompleters);
}
await instance.initialize();
_logger.info("${typeName} initialized");
}
completers[typeName]!.complete();
}
final initializers = sorted
.map(
(dep) => initializeDependency(dep.getTypeName()),
)
.toList();
await Future.wait(initializers);
return this;
}