我第一次尝试使用 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
依赖项来解决此问题,但这似乎非常不安全,因为系统中的任何代码都可以随意改变依赖项。
希望了解其他人如何通过这种方法解决看似非常基本的问题。
引用
- http://merowing.info/2017/04/using-protocol-compositon-for-dependency-injection/
- https://swiftwithmajid.com/2019/03/06/dependency-injection-in-swift-with-protocols/
你可以使用private(set(lazy var:
private(set) lazy var networking: NetworkingProtocol = {
return Networking(dependencies: self)
}()
PS:我发布了原始问题。我已经接受了lazy var
答案,这是最简单的方法,但是我想发布这个使用通用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)
}
}