依赖注入是一种让代码低耦合的设计模式。
有四种实现方式:

  • 初始化注入
  • 属性赋值注入
  • 接口注入
  • 容器注入

    初始化注入

    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() {
    // Does something
    }
    }

    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() {
// Does something
}
}

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() {
// Does something with a dependency
}
}

class Injector {
typealias Client = HasDependency & DoesSomething
private var clients: [Client] = []

func inject(_ client: Client) {
clients.append(client)
client.setDependency(SomeDependency())
// Dependency applies its policies over clients
client.doSomething()
}

// Switch dependencies under certain conditions
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 {}

评论