一 控制反转与依赖注入

​ 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
    49
    package 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进行嵌入,使得业务对象对控制对象的依赖,在实现功能的完备性上,必须依赖控制对象,而控制对象,只需要将注入进行的业务对象添加到抽象协议中,即可,简化了业务和控制逻辑。