IoC在Go中的应用
一 控制反转与依赖注入
IoC(Ioc—Inversion of Control)控制反转,是一种设计思想,控制反转意味着可以不用在内部进行业务对象的实例化,而是将控制逻辑进行高度抽象,抽象成为一种具有一定签名的协议,将业务对象进行注入,在内部消除了控制逻辑对某一种具体对象的依赖。
在这样的设计方式中,直接将业务对象中进行控制抽象,转化为业务对象对控制协议的依赖。而控制逻辑通过抽象成协议,将对具象业务对象的依赖进行解耦,控制协议不关心到底是什么样的业务对象,只要满足协议签名的任何对象,通过依赖注入的方式,进行具象业务控制。
二 Go中控制反转的案例分析
实现一个数据存放和获取的容器,这个容器具有获取(get)、存放(put)的方法,其实现代码如下。
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
42
43
44
45
46
47
48
49package main
import (
"fmt"
"sync"
)
/*
@Time : 2021/1/19 21:13
@Author : austsxk
@Email : austsxk@163.com
@File : container.go
@Software: GoLand
*/
// 容器,可以存放任何类型的数据信息
type Container struct {
len int
mtx sync.Mutex
dt []interface{}
}
// 存放的方法
func (c *Container) Put(d interface{}) {
c.mtx.Lock()
defer c.mtx.Unlock()
c.dt = append(c.dt, d)
c.len++
}
// 取出获取的最后一个方法
func (c *Container) Pull() interface{} {
c.mtx.Lock()
defer c.mtx.Unlock()
if c.len == 0 {
return nil
}
result := c.dt[len(c.dt)-1]
c.dt = c.dt[:len(c.dt)-1]
c.len--
return result
}
func NewContainer() *Container {
return &Container{}
}在上面容器中增加可撤销的控制逻辑 CancelAble,就是存放之后,如果撤销就是取出之前存放的元素,如果是取出,撤销操作就是将元素放回,最多可撤销10次;
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
42
43
44
45
46// CancelAbleContainer 组合原来的struct
// 由于在放和拿有了可以撤销的操作,也就是需要重载存放和获取的方法
type CancelAbleContainer struct {
Container
fns []func()
}
// 重载Put方法,如果不重载,则调用的是嵌套对象的方法 obj.Put 就是 obj.Container.Put
func (c *CancelAbleContainer) Put(in interface{}) {
// 存放数据
c.Container.Put(in)
// 可撤销函数
fn := func() {c.Container.Pull()}
if len(c.fns) < 10 {
c.fns = append(c.fns, fn)
} else {
c.fns = append(c.fns[1:], fn)
}
}
func (c *CancelAbleContainer) Pull() interface{} {
data := c.Container.Pull()
// 可撤销函数
fn := func() {c.Container.Put(data)}
if len(c.fns) < 10 {
c.fns = append(c.fns, fn)
} else {
c.fns = append(c.fns[1:], fn)
}
return data
}
// 可撤销操作
func (c *CancelAbleContainer) Cancel() error {
if len(c.fns) == 0 {
return errors.New("No Data Can Canceled!")
}
index := len(c.fns) - 1
c.fns[index]()
c.fns = c.fns[:index]
return nil
}
func NewCancelAbleContainer() *CancelAbleContainer {
return &CancelAbleContainer{}
}说明: 上面我们重载了获取和存放的方法,实例对象直接调用Put或者Pull方法就是调用的CancelAbleContainer.Put 和CancelAbleContainer.Pull方法。新增加了撤销操作,可以撤销10次,这其实是对Container对象新加的控制逻辑,在上面内容中,显示在控制逻辑中,强耦合了业务对象Container的业务逻辑,如果需要对其他对象进行控制,显示是存在难拓展性。
通过对控制逻辑进行抽象,将其抽象为一种协议,然后让业务对象对控制进行依赖,将依赖对象进行注入方式实现可撤销的存储容器。
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60// 自定义一个撤销类型 其类型为函数切片
type Cancel []func()
// 对控制逻辑抽象为Cancel协议,有撤销函数,与给类型添加需要注入的依赖
func (c *Cancel) Add(f func()) {
*c = append(*c, f)
}
// Cancel函数,用来进行撤销的操作,这个方法是抽象协议的方法,不属于具象对象,不依赖任何业务对象
func (c *Cancel) CancelFunc() error {
p := *c
if len(p) == 0 {
return errors.New("No Data Can Canceled!")
}
p[len(p)-1]()
*c = p[:len(p)-1]
return nil
}
type NCancelAbleContainer struct {
len int
mtx sync.Mutex
dt []interface{}
// 控制逻辑
cancel Cancel
}
// 此处业务对象撤销需要依赖控制抽象对象,进行控制反转
func (n *NCancelAbleContainer) Cancel() error {
return n.cancel.CancelFunc()
}
func (n *NCancelAbleContainer) Put(i interface{}) {
n.mtx.Lock()
defer n.mtx.Unlock()
n.dt = append(n.dt, i)
n.len++
// 添加可撤销操作
fn := func() { n.Pull() }
n.cancel.Add(fn)
}
func (n *NCancelAbleContainer) Pull() interface{} {
n.mtx.Lock()
defer n.mtx.Unlock()
if n.len == 0 {
return nil
}
result := n.dt[len(n.dt)-1]
n.dt = n.dt[:len(n.dt)-1]
n.len--
fn := func() { n.Put(result) }
n.cancel.Add(fn)
return result
}
func CreateCancelContainer() *NCancelAbleContainer {
return &NCancelAbleContainer{}
}说明一下:
我们定义一个Cancel类型,抽象控制协议,将控制需要传入的依赖,全部抽象,在这个类型中,我们只要满足func()类型的签名即可,不用管是什么业务对象都可以进行注入,这就是将业务对象进行解耦,同时增加了可拓展性,任何满足业务需求的,处理成协议格式的,都可以使用Cancel函数进行实现。在业务对象中,我们通过cancel进行嵌入,使得业务对象对控制对象的依赖,在实现功能的完备性上,必须依赖控制对象,而控制对象,只需要将注入进行的业务对象添加到抽象协议中,即可,简化了业务和控制逻辑。