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

Swift 更好地理解依赖注入-解析新实例?,swift,dependency-injection,inversion-of-control,ioc-container,Swift,Dependency Injection,Inversion Of Control,Ioc Container,我一直在从事一项工作,要求我专注于依赖注入。对于后人来说,我在Swift/SwiftUI中使用了这一点,尽管我相信我对这一概念的理解不足比语言本身更为固有 我已经创建了一个依赖注入容器,可以用来注册和解析类型和组件。因此 protocol MyContainerProtocol { func register<Component>(type: Component.Type, component: Any) func resolve<Component>(t

我一直在从事一项工作,要求我专注于依赖注入。对于后人来说,我在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
    }
}
在应用程序生命周期的早期,我正在注册该组件。比如,

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
有它自己的依赖项,这使我不确定如何注册类型


我不确定我的问题是否存在于依赖项容器的构建方式、类的构建方式,或者我只是不理解被问到的问题。即使查看流行的Swift库,如或,我也不完全了解我的容器有何不当之处(或者这是否是工厂方法的用武之地)。

您需要添加一个额外的寄存器函数

protocol-mycontainer协议{
func寄存器(类型:组件。类型,组件:任意)
func寄存器(类型:Component.type,生成器:@escaping(MyContainerProtocol)->Component)
函数解析(类型:Component.type)->Component?
}
最后一类MyContainer:MyContainer协议{
静态let shared=MyContainer()
私有init(){}
变量组件:[字符串:任意]=[:]
func寄存器(类型:组件。类型,组件:任意){
组件[“\(类型)”]=组件
}
func寄存器(类型:Component.type,生成器:@escaping(MyContainerProtocol)->Component){
组件[“\(类型)”]=生成器
}
函数解析(类型:Component.type)->Component{
如果让singleton=components[“\(type)”]作为组件{
返回单身
}
如果让生成器=组件[“\(类型)”]作为?(MyContainerProtocol)->Component{
返回生成器(自身)
}
归零
}
}
然后在呼叫站点上会显示如下内容:

结构动物{ let类型:String 设id=UUID() } 结构人{ let name:String 让宠物:动物 设id=UUID() } 类复杂化网络堆栈{ 设id=UUID() ///这里有这么多东西 } MyContainer.shared.register(类型:Animal.self){in Animal(类型:“Dog”)} MyContainer.shared.register(类型:Person.self){container in 人( 姓名:“乔·迪特”, 宠物:容器。解析(类型:动物。自我)! ) } MyContainer.shared.register(类型:complexDNetworkStack.self,组件:complexDNetworkStack())
如果您在操场上运行该代码并多次解析
Person
Animal
,您将看到UUID都是不同的,而
复杂网络堆栈的id是相同的。

如果您每次都需要创建一个新实例,您可以注册一个
(DependencyContainer)->组件
功能,而不是已构造的对象。依赖项容器部分在那里,因此您可以解析已注册的任何依赖项。您是否绝对有必要从头开始构建它,而不是使用库?我从未亲自使用过Swinject或DIP,但他们可能已经解决了您正在或将要自己构建的许多问题。谢谢您的回复@Steven0351。如果我跟随你的评论,你是建议在我的依赖容器中创建一个新函数,当调用该函数时,返回给定类的一个新实例化实例?这在实践中听起来很简单,但根据我的VideoProcessor示例,当一个实例本身有依赖项(而不是变量)时,我将如何处理返回该实例?你能提供一个例子吗?我发布了一个答案,这样我可以提供一个比我在评论中更详细的例子。谢谢你,@Steven0351!我正在复习你的答案,努力消化知识并尝试一下。谢谢你的帮助!谢谢你的回复,@Steven0351。如果你愿意回答,我对“为什么”和“如何”有点好奇。我真的不明白为什么有人会想要这个实现,因为我忍不住认为依赖注入的原则是提供单一的真相来源,而不允许创建同一类型的多个真相。此外,我不确定这与我的示例在原则上有什么不同,因为我可以将
let id=UUID()
添加到我的
VideoProcessor
类中,它似乎也会做同样的事情
只是一段代码,用于说明每次创建新对象时会产生什么内容,以及作为已构造对象传递的内容。就我个人而言,我不会在iOS应用程序中使用依赖注入。我所有的经验都来自于服务器端的Java和Kotlin,依赖注入是一种常见的模式。不过,我只对执行数据库调用、出站http请求等操作的对象使用依赖项注入。当您要测试这些对象时,您只需将mock注入共享容器中即可。谢谢@Steven0351。我也有同感;我没有在iOS中大量使用依赖项注入,尤其是SwiftUI的出现,它使用环境对象处理这些概念,而不需要容器。这是一个很好的方式来重新确认我的知识,我感谢您的样品和细节。
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"