更好地理解依赖注入 - 解决新实例?

Better understanding of Dependency Injection - Resolving New Instances?

我的工作要求我专注于依赖注入。对于后代,我在 Swift/SwiftUI 中使用它,尽管我相信我对概念的理解比语言更固有。

我创建了一个依赖注入容器,它可以用来注册和解析类型和组件。因此;

protocol MyContainerProtocol {
    func register<Component>(type: Component.Type, component: Any)
    func resolve<Component>(type: Component.Type) -> Component?
}

final class MyContainer: MyContainerProtocol {
    
    static let shared = DependencyContainer()
    private init() { }
    
    var components: [String: Any] = [:]
    
    func register<Component>(type: Component.Type, component: Any) {
        components["\(type)"] = component
    }
    
    func resolve<Component>(type: Component.Type) -> Component? {
        return components["\(type)"] as? Component
    }
}

这将在下面变得相关,但我的项目中有一个 class,名为 VideoProcessor;

class VideoProcessor: SomeProtocol {
    var codec: String
    var format: String

    init(codec: String, format: String) {
      self.codec = codec
      self.format = format
    }
}

在应用程序生命周期的早期,我正在注册组件。例如;

let container = DependencyContainer.shared
container.register(type: VideoProcessor.self, component: VideoProcessor(codec: "H264", format: "MP4"))
...
let processor = container.resolve(type: VideoProcessor.self)!

我的困惑: 我被问到的是解析一个类型的实例,而不必在注册时构造它。实际上,每次解析注册类型时,我都会被要求解析一个新实例。在我看来,这意味着我的代码类似于;

let container = DependencyContainer.shared
container.register(type: VideoProcessor.self)
...
let processorA = container.resolve(type: VideoProcessor.self)!
processorA.codec = "H264"
processorA.format = "MP4"

let processorB = container.resolve(type: VideoProcessor.self)!
processorB.codec = "H265"
processorB.format = "MOV"

但是,VideoProcessor 有其自身的依赖性,导致我不确定如何注册类型。

我不确定我的问题是否存在于我的依赖容器的构建方式中,我的 classes 的构建方式中,或者如果我只是不理解被问到的问题.即使查看像 Swinject or DIP 这样流行的 Swift 库,我也没有完全看出我的 Container 做错了什么(或者如果这是工厂方法的用武之地)。

您需要添加一个额外的注册函数。

protocol MyContainerProtocol {
  func register<Component>(type: Component.Type, component: Any)
  func register<Component>(type: Component.Type, builder: @escaping (MyContainerProtocol) -> Component)
  func resolve<Component>(type: Component.Type) -> Component?
}

final class MyContainer: MyContainerProtocol {
  
  static let shared = MyContainer()
  private init() { }
  
  var components: [String: Any] = [:]
  
  func register<Component>(type: Component.Type, component: Any) {
    components["\(type)"] = component
  }
  
  func register<Component>(type: Component.Type, builder: @escaping (MyContainerProtocol) -> Component) {
    components["\(type)"] = builder
  }
  
  func resolve<Component>(type: Component.Type) -> Component? {
    if let singleton = components["\(type)"] as? Component {
      return singleton
    }
    
    if let builder = components["\(type)"] as? (MyContainerProtocol) -> Component {
      return builder(self)
    }
    
    return nil
  }
}

然后在调用站点看起来像这样:

struct Animal {
  let type: String
  let id = UUID()
}

struct Person {
  let name: String
  let pet: Animal
  let id = UUID()
}

class ComplicatedNetworkStack {
  let id = UUID()
  /// so much stuff in here
}

MyContainer.shared.register(type: Animal.self) { _ in Animal(type: "Dog") }
MyContainer.shared.register(type: Person.self) { container in
  Person(
    name: "Joe Dirt",
    pet: container.resolve(type: Animal.self)!
  )
}

MyContainer.shared.register(type: ComplicatedNetworkStack.self, component: ComplicatedNetworkStack())

如果你在 playground 中 运行 该代码并解析 PersonAnimal 几次,你会看到 UUID 都是不同的,而 ComplicatedNetworkStack的id是一样的