依赖注入是一种让代码低耦合的设计模式。
有四种实现方式:
- 初始化注入
- 属性赋值注入
- 接口注入
- 容器注入
初始化注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| protocol Dependency { func foo() }
struct DependencyImplementation: Dependency { func foo() { } }
class Client { let dependency: Dependency
init(dependency: Dependency) { self.dependency = dependency }
func foo() { dependency.foo() } }
let client = Client(dependency: DependencyImplementation()) client.foo()
|
初始化注入后,属性不可再被更改。当超过三个以上的属性需要注入,那么这个时候适合用属性注入的方式。
属性注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| protocol Dependency { func foo() }
struct DependencyImplementation: Dependency { func foo() { } }
class Client { var dependency: Dependency!
func foo() { dependency.foo() } }
let client = Client() client.dependency = DependencyImplementation() client.foo()
|
这种方式允许延后注入,但存在因某属性未注入而调用失败的问题。为了防止对象未注入的情况造成业务失败或者程序异常,接下来需要用接口对注入进行协议约定。
接口注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| protocol Dependency {}
protocol HasDependency { func setDependency(_ dependency: Dependency) }
protocol DoesSomething { func doSomething() }
class Client: HasDependency, DoesSomething { private var dependency: Dependency!
func setDependency(_ dependency: Dependency) { self.dependency = dependency }
func doSomething() { } }
class Injector { typealias Client = HasDependency & DoesSomething private var clients: [Client] = []
func inject(_ client: Client) { clients.append(client) client.setDependency(SomeDependency()) client.doSomething() }
func switchToAnotherDependency() { clients.forEach { $0.setDependency(AnotherDependency()) } } }
class SomeDependency: Dependency {} class AnotherDependency: Dependency {}
|
但client本身就是一个依赖项,使得控制流变得复杂了。
Ambient Context
单个全局可访问的依赖项,通过协议公开。这允许在需要时替代实现,例如在测试中。我们可以根据需要灵活地替换DateTimeProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| protocol DateTimeProvider { var now: Date { get } }
struct SystemDateTimeProvider: DateTimeProvider { var now: Date { return Date() } }
class DateTime { static var provider: DateTimeProvider = SystemDateTimeProvider()
static var now: Date { return provider.now } }
|
工厂模式构建依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| protocol Client {}
enum ClientFactory {
static func make() -> Client { return ClientImplementation(dependency: DependencyImplementation()) } }
class ClientImplementation: Client { init(dependency: Dependency) {} }
protocol Dependency { } struct DependencyImplementation: Dependency {}
|