build method

Future<Dependencies> build()

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 = <Type, List<Type>>{};
  final inDegrees = <Type, int>{};

  for (final dep in _dependencies) {
    graph[dep.type] = [];
    inDegrees[dep.type] = 0;
  }

  for (final dep in _dependencies) {
    for (final dependency in dep.dependsOn) {
      if (graph.containsKey(dependency) == false) {
        throw StateError(
          "Dependency '$dependency' not found for '${dep.type}'.",
        );
      }

      graph[dependency]!.add(dep.type);
      inDegrees[dep.type] = (inDegrees[dep.type] ?? 0) + 1;
    }
  }

  final queue = Queue<Type>();
  final sorted = <Dependency<Object>>[];

  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.type == 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) {
    log("Instantiating", name: "${dependency.type}");

    final instance = dependency.factory(this);

    if (instance is IInitializable) {
      log("Initializing", name: "${dependency.type}");
      await instance.initialize();
    }

    _instances[dependency.type] = MapEntry(this, instance);
  }

  return this;
}