Swift 如何将单例模式与依赖项注入结合使用?
我最近听说,使用依赖注入是“当今软件开发世界中唯一社会上可以接受的使用单例的方法”。我现在不一定要对这一说法的准确性进行辩论,因为它主要是基于观点的。我现在的目标是了解如何准确地将依赖项注入与单例模式结合使用 例如,在我最新的iOS应用程序中,我有一个服务层,在其中保存我的URLSession代码。我将此层创建为单体:Swift 如何将单例模式与依赖项注入结合使用?,swift,design-patterns,dependency-injection,singleton,Swift,Design Patterns,Dependency Injection,Singleton,我最近听说,使用依赖注入是“当今软件开发世界中唯一社会上可以接受的使用单例的方法”。我现在不一定要对这一说法的准确性进行辩论,因为它主要是基于观点的。我现在的目标是了解如何准确地将依赖项注入与单例模式结合使用 例如,在我最新的iOS应用程序中,我有一个服务层,在其中保存我的URLSession代码。我将此层创建为单体: struct ServiceSingleton { private init() static let shared = ServiceSingleton()
struct ServiceSingleton {
private init()
static let shared = ServiceSingleton()
func fetchJSON() {
// URLSession code
}
}
然后,我在我的ViewController中使用shared
,如下所示:
class ViewController: UIViewController() {
override viewDidLoad() {
super.viewDidLoad()
fetchData()
}
fileprivate func fetchData() {
ServiceSingleton.shared.fetchJSON()
}
}
当然,上面的代码使用单例,但它不使用依赖项注入。我知道,如果我想在一般情况下使用依赖项注入,我会在ViewController中添加如下内容:
TL;医生:
(1) 您能告诉我如何在Swift中正确使用依赖项注入和单例模式吗
(2) 你能给我解释一下这是怎么实现的吗
(3) 从现在开始,当我在iOS项目中使用单例模式时,我应该一直使用DI吗
ServiceSingleton.shared
不同,您可以访问注入对象的实例变量,如果可能,通常在初始值设定项中,否则作为可设置属性,在初始化后:
protocol FooService {
func doFooStuff()
}
class ProductionFooService: FooService {
private init() {}
static let shared = ProductionFooService()
func doFooStuff() {
print("real URLSession code goes here")
}
}
struct MockFooService: FooService {
func doFooStuff() {
print("Doing fake foo stuff!")
}
}
class FooUser {
let fooService: FooService
init(fooService: FooService) { // "initializer based" injection
self.fooService = fooService
}
func useFoo() {
fooService.doFooStuff() // Doesn't directly call ProductionFooService.shared.doFooStuff
}
}
let isRunningInAUnitTest = false
let fooUser: FooUser
if !isRunningInAUnitTest {
fooUser = FooUser(fooService: ProductionFooService.shared) // In a release build, this is used.
}
else {
fooUser = FooUser(fooService: MockFooService()) // In a unit test, this is used.
}
fooUser.useFoo()
通常情况下,ViewController的初始化由情节串连板完成,因此您不能通过初始化器参数来确定依赖项,而必须使用对象初始化后设置的存储属性ProductionFooService.shared
。因此,您可以引入FooService
的不同实现,例如用于beta环境的实现,用于单元测试的模拟实现,等等
如果所有代码都直接使用产品依赖项,那么您将
ServiceSingleton.shared
不同,您可以访问注入对象的实例变量,如果可能,通常在初始值设定项中,否则作为可设置属性,在初始化后:
protocol FooService {
func doFooStuff()
}
class ProductionFooService: FooService {
private init() {}
static let shared = ProductionFooService()
func doFooStuff() {
print("real URLSession code goes here")
}
}
struct MockFooService: FooService {
func doFooStuff() {
print("Doing fake foo stuff!")
}
}
class FooUser {
let fooService: FooService
init(fooService: FooService) { // "initializer based" injection
self.fooService = fooService
}
func useFoo() {
fooService.doFooStuff() // Doesn't directly call ProductionFooService.shared.doFooStuff
}
}
let isRunningInAUnitTest = false
let fooUser: FooUser
if !isRunningInAUnitTest {
fooUser = FooUser(fooService: ProductionFooService.shared) // In a release build, this is used.
}
else {
fooUser = FooUser(fooService: MockFooService()) // In a unit test, this is used.
}
fooUser.useFoo()
通常情况下,ViewController的初始化由情节串连板完成,因此您不能通过初始化器参数来确定依赖项,而必须使用对象初始化后设置的存储属性ProductionFooService.shared
。因此,您可以引入FooService
的不同实现,例如用于beta环境的实现,用于单元测试的模拟实现,等等
如果所有代码都直接使用产品依赖项,那么您将
当然可以你能开个聊天室和我聊天吗?我现在有空,但我要走了soon@AlwaysLearning好的,等你有机会的时候就开始聊天室,提前输入你的问题,提到我的用户名,等我有空的时候我会及时回复。库尔