依赖注入的协议组合 - 因果困境/编译问题

Protocol Composition for Dependency Injection - Causality Dilemma / Compile Issue

我第一次尝试使用 Swift "Protocol Composition" 进行依赖注入。该领域备受尊敬的工程师发表了各种博客文章提倡这种方法,但是一旦存在依赖于其他依赖项的依赖项,我就无法编译代码。

问题是,在初始化主要具体 AllDependencies 实例之前,它不能用于初始化子依赖项,但相反,如果没有具体 AllDependencies 实例,则无法创建子依赖项。

鸡和蛋。岩石和坚硬的地方。

我会尽力提供最简单的示例...

protocol HasAppInfo {
    var appInfo: AppInfoProtocol { get }
}
protocol AppInfoProtocol {
    var apiKey: String { get }
}
struct AppInfo: AppInfoProtocol {
    let apiKey: String
}

protocol HasNetworking {
    var networking: NetworkingProtocol { get }
}
protocol NetworkingProtocol {
    func makeRequest()
}
class Networking: NetworkingProtocol {

    typealias Dependencies = HasAppInfo

    let dependencies: Dependencies

    init(dependencies: Dependencies) {
        self.dependencies = dependencies
    }

    func makeRequest() {
        let apiKey = self.dependencies.appInfo.apiKey
        // perform request sending API Key
        // ...
    }
}

class AllDependencies: HasAppInfo, HasNetworking {

    let appInfo: AppInfoProtocol
    let networking: NetworkingProtocol

    init() {
        self.appInfo = AppInfo(apiKey: "whatever")

        /// **********************************************************
        /// *** ERROR: Constant 'self.networking' used before being initialized
        /// **********************************************************
        self.networking = Networking(dependencies: self)
    }

}

似乎可以通过使用 lazy var{get set}mutating 依赖项来解决此问题,但这似乎非常不安全,因为您系统中的任何代码都可能改变您的随意依赖。

希望了解其他人如何使用这种方法解决看似非常基本的问题。

参考资料

您可以使用私有(集合)惰性变量:

private(set) lazy var networking: NetworkingProtocol = {
    return Networking(dependencies: self)
}()

PS:我post编辑了原来的问题。我已经接受了lazy var答案,这是最简单的前进方式,但是我想 post 这个使用通用 DependencyFactory 的替代解决方案,以防它帮助其他人。

protocol Dependencies:
    HasAppInfo &
    HasNetworking
{}


class DependencyFactory {

    typealias Factory<T> = (Dependencies) -> T

    private enum DependencyState<T> {
        case registered(Factory<T>)
        case initialised(T)
    }

    private var dependencyStates = [String: Any]()

    func register<T>(_ type: T.Type, factory: @escaping Factory<T>) {
        dependencyStates[key(for: type)] = DependencyState<T>.registered(factory)
    }

    func unregister<T>(_ type: T.Type) {
        dependencyStates[key(for: type)] = nil
    }

    func resolve<T>(_ type: T.Type, dependencies: Dependencies) -> T {
        let key = self.key(for: type)

        guard let dependencyState = dependencyStates[key] as? DependencyState<T> else {
            fatalError("Attempt to access unregistered `\(type)` dependency")
        }

        switch dependencyState {

        case let .registered(factoryClosure):
            let dependency = factoryClosure(dependencies)
            dependencyStates[key] = DependencyState<T>.initialised(dependency)
            return dependency

        case let .initialised(dependency):
            return dependency

        }
    }

    private func key<T>(for type: T.Type) -> String {
        return String(reflecting: type)
    }
}


class AllDependencies: Dependencies {

    private let dependencyFactory = DependencyFactory()

    init() {
        dependencyFactory.register(AppInfoProtocol.self, factory: { dependencies in
            return AppInfo(apiKey: "whatever")
        })

        dependencyFactory.register(NetworkingProtocol.self, factory: { dependencies in
            return Networking(dependencies: dependencies)
        })
    }

    var appInfo: AppInfo {
        return dependencyFactory.resolve(AppInfoProtocol.self, dependencies: self)
    }

    var networking: Networking {
        return dependencyFactory.resolve(NetworkingProtocol.self, dependencies: self)
    }
}