反转控制

概念:控制逻辑与业务逻辑分享,让业务逻辑依赖控制逻辑。

大概的思路就是***将控制代码剥离,然后在逻辑代码中去调用控制代码即可***,具体实现的时候,就是在逻辑代码中内嵌控制代码的类型,这样即可。

首先需要明确的是 “控制逻辑是什么” 这个问题,我们举一个例子:有一个数据库,数据库的增删改查就是控制逻辑,使用这些基础的动作去将用户的名字存入数据库,删除用户的名字,这叫业务逻辑,我们所说的反转控制,就是将基础的控制剥离出来,下面我们看一个例子:

// 这是控制代码,我们把控制代码先揪出来。
type Undo []func()

func (undo *Undo) Add(function func()) {
  *undo = append(*undo, function)
}
// 这段代码的作用:恢复
func (undo *Undo) Undo() error {
  functions := *undo
  if len(functions) == 0 {
    return errors.New("No functions to undo")
  }
  index := len(functions) - 1
  if function := functions[index]; function != nil {
    function()
    // For garbage collection
    functions[index] = nil 
  }
  // 这里是为了规避在调用delelte的时候,又调用了add的情况
  // 因为 delete的时候会调用add,add的时候会再次调用delete
  // 为了防止这种情况,我们认为的将这个切片减去一个即可。
  *undo = functions[:index]
  return nil
}

我们将控制逻辑嵌入到业务逻辑中

type IntSet struct {
    data map[int]bool
    undo Undo
}
 
func NewIntSet() IntSet {
    return IntSet{data: make(map[int]bool)}
}

// 直接继承
func (set *IntSet) Undo() error {
    return set.undo.Undo()
}
 
func (set *IntSet) Contains(x int) bool {
    return set.data[x]
}
func (set *IntSet) Add(x int) {
    if !set.Contains(x) {
        // 增加一个数据
        set.data[x] = true
        // 如果是恢复的话,是需要删除一个数据的
        // 所以这里的操作就是删除
        set.undo.Add(func() { set.Delete(x) })
    } else {
        set.undo.Add(nil)
    }
}
 
func (set *IntSet) Delete(x int) {
    if set.Contains(x) {
        delete(set.data, x)
        set.undo.Add(func() { set.Add(x) })
    } else {
        set.undo.Add(nil)
    }
}