反转控制
概念:控制逻辑与业务逻辑分享,让业务逻辑依赖控制逻辑。
大概的思路就是***将控制代码剥离,然后在逻辑代码中去调用控制代码即可***,具体实现的时候,就是在逻辑代码中内嵌控制代码的类型,这样即可。
首先需要明确的是 “控制逻辑是什么” 这个问题,我们举一个例子:有一个数据库,数据库的增删改查就是控制逻辑,使用这些基础的动作去将用户的名字存入数据库,删除用户的名字,这叫业务逻辑,我们所说的反转控制,就是将基础的控制剥离出来,下面我们看一个例子:
// 这是控制代码,我们把控制代码先揪出来。
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)
}
}