闭包的概念、形式与应用一文中,有一总结“对象是附有行为的数据,而闭包是附有数据的行为”。
先看两个例子——
第一个例子来自Go中的闭包
package main
import "fmt"
func closure01() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
myfun := closure01()
for i := 0; i < 3; i++ {
fmt.Println(myfun(i))
//调用 myfun 会执行closure01()内部的匿名函数,i作为参数传递给该函数的变量x,sum就是“附有的数据”
}
}
输出:
[root@datanode1 demo]# go run closure_demo.go
0
1
3
上面这个例子是将变量,放到引用环境中。再看一个例子,将函数放到引用环境中(其实在go中,函数也是一种变量,即函数是一阶值)。
package main
import "fmt"
func closure02(fn func(int, int) int) func(int) int {
tmp := 1
return func(x int) int {
return fn(x, tmp)
}
}
func add(x int, y int) int {
return x + y
}
func sub(x int, y int) int {
return x - y
}
func main() {
// closure02 稍微复杂一点 —— 它的参数中有一函数类型的变量fn,在内部的匿名函数里调用了fn, 这里嵌套了一层
myadd := closure02(add)
fmt.Println(myadd(1))
// 调用 myadd 会执行 closure02 内部的匿名函数,匿名函数又会执行作为参数传递的函数fn, 函数fn的参数x就是myadd(1)中的1,参数y就是附有数据tmp,所以最终结果就是 x(1) + y(tmp=1) = 2
mysub := closure02(sub)
fmt.Println(mysub(1))
// 同上
}
输出:
[root@datanode1 demo]# go run closure_demo.go
2
0
为什么要考虑函数放在引用环境中这种场景呢?在应用中,不同的url通常是由不同的handler处理,这一步是通过函数http.HandleFunc
定义的
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
我们所要做的就是针对不同的pattern,定义不同的handler就行了,很简单有木有。问题是如果我们的handler函数不是这种类型func(ResponseWriter, *Request)
,该怎么办呢?
例如,我们在logic
包中有这样一个处理器MyHandler
:
func MyHandler(req *protocol.ClientRequest, w http.ResponseWriter, r *http.Request, ch chan string)
解决的方式就是定义一个闭包makeHttpHandler
,将我们的处理器MyHandler
作为参数fn
放到引用环境,在内部匿名函数中调用处理器fn
:
func makeHttpHandler(
fn func(*protocol.ClientRequest, http.ResponseWriter, *http.Request, chan string),
reqId string,
ch chan string
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
...
fn(someClientRequest, w, r, ch)
...
}
}
这样一来,就可以像通常那样来定义处理器了
http.HandleFunc("/some/pattern", makeHttpHandler(logic.MyHandler, "myString", myChan))
2014-03-21@迈科龙
评论