一、有名函数和匿名函数

func (InputTypeList) OutputTypeList
//无参函数
func fun() {   
}
var f func()//无入参无返回值的函数对象声明,初始值为nil
f = fun
//有参函数
type FT func(int)
func Fa(int){}
func Test(FT){}
Test(Fa) //pass function as parameter
type NewType OldTypetype NewFuncType FuncLiteral
type CalculateType func(int, int) // 声明了一个函数类型
// 该函数类型实现了一个方法
func (c *CalculateType) Serve() {
  fmt.Println("我是一个函数类型")
}
// 加法函数
func add(a, b int) {
  fmt.Println(a + b)
}
// 乘法函数
func mul(a, b int) {
  fmt.Println(a * b)
}
func main() {
  a := CalculateType(add) // 将add函数强制转换成CalculateType类型
  b := CalculateType(mul) // 将mul函数强制转换成CalculateType类型
  a(2, 3)
  b(2, 3)
  a.Serve()
  b.Serve()
}

二、方法作为函数变量传递

当特定对象实例的方法method作为函数指针时传递时,接受者会保证在调用的时候,调用到是这个对象实例的method,method的任何操作都会针对该对象实例生效,而且不需要传任何类似于this、self指针之类的东西,换句话说,对象实例+method 作为绑定的整体传递给接受者的

type Outer struct{
    a string
}
func (o *Outer)Hello(in *Inner){
   fmt.Println("Inner:",in,"\tcall\tOuter:",o.a)
}
type Inner struct{
        a string
    cb Cb
}
type Cb func(*Inner)
func (in *Inner)Register(cb Cb){
    in.cb = cb
}
func (in *Inner)Say(){
    in.cb(in)
}

func main() {
    out1 := Outer{a:"out1 instance"}
    out2 := Outer{a:"out2 instance"}
    fmt.Println("out1 :",&out1)
    fmt.Println("out2 :",&out2) 
    in1 := Inner{a:"int1 instance"}
    in1.Register(out1.Hello)
    in1.Say()
    in1.Register(out2.Hello)
    in1.Say()
}

三、闭包构成的三种情况

闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。

// 第一种场景
func fib01() func() int {
	return func() int {
		a, b := 0, 1
		a, b = b, a+b
		return a
	}
}
var y int
// 第二种场景
func fib00() func() int {
	return func() int {
		y++
		return y
	}
}
func AntherExFunc(n int) func() {
    n++
    return func() {
        fmt.Println(n)
    }
}

func ExFunc(n int) func() {
    return func() {
        n++
        fmt.Println(n)
    }
}

func main() {
    myAnotherFunc:=AntherExFunc(20)
    fmt.Println(myAnotherFunc)  //0x48e3d0  在这儿已经定义了n=20 ,然后执行++ 操作,所以是21 。
    myAnotherFunc()     //21 后面对闭包的调用,没有对n执行加一操作,所以一直是21
    myAnotherFunc()     //21
    myFunc:=ExFunc(10)
    fmt.Println(myFunc)  //0x48e340   这儿定义了n 为10
    myFunc()       //11  后面对闭包的调用,每次都对n进行加1操作。
    myFunc()       //12
}

四、for循环中的并发闭包:

//因为for语句里面中闭包使用的v是外部的v变量,当执行完循环之后,v最终是c,所以如果在主协程执行完for之后,定义的子协程才开始执行结果可能是ccc,
//如果for过程中,子协程先执行了,结果就可能不是c, c,c”。 
func test1() {                
    s := []string{"a", "b", "c"}                             
    for _, v := range s { 
        go func() {
            fmt.Println(v)
        }()                 
    }                        
    time.Sleep(time.Second * 1)                                                       
}
//for程序如果想输出a,b,c的解决方法:
//只需要每次将变量v的拷贝传进函数即可,但此时就不是使用的上下文环境中的变量了。
func test2() {                
    s := []string{"a", "b", "c"}                             
    for _, v := range s { 
        go func(v string) {
            fmt.Println(v)
        }(v)   //每次将变量 v 的拷贝传进函数                 
    }                        
    select {}                                                      
}

//结果为4 4 4 4 4,函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4
func test1() {
    var users [5]struct{}
    for i := range users {
        defer func() { fmt.Println(i) }()
    }
}
//defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份利用函数参数拷贝,输出为 4 3 2 1 0
func test1() {
    var users [5]struct{}
    for i := range users {
        defer Print(i)
    }
}
func Print(i int) {
    fmt.Println(i)
}

select语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select会随机地选取其中之一执行。select {}会一直阻塞。很多时候我们需要让main函数不退出,利用select {}让它在后台一直执行

func main() {
    for i := 0; i < 20; i++ { //启动20个协程处理消息队列中的消息
        c := consumer.New()
        go c.Start()
    }
    select {} // 阻塞
}

五、闭包的应用场景和作用:

1、延迟调用,关键字 defer 用于注册延迟调用。defer 调用会在当前函数执行结束前才被执行,这些调用被称为延迟调用 。defer 中使用匿名函数依然是一个闭包。

func main() {
    x, y := 1, 2
    defer func(a int) { 
        fmt.Printf("x:%d,y:%d\n", a, y)  // y 为闭包引用
    }(x)      // 复制 x 的值
    x += 100
    y += 100
    fmt.Println(x, y)
}

2、变量空间隔离,避免变量污染外部无法对闭包引用的上下文变量进行直接操作,保证了私有性。

// 函数计数器  利用闭包每个计数器有自己独立未暴露的sum
func counter(f func()) func() int {  
	sum := 0
	return func() int {
		f()
		sum += 1
		return sum
	}
}

// 测试的调用函数
func foo() {
	fmt.Println("call foo")
}

func main() {
	cnt := counter(foo)
	cnt()
	cnt()
	cnt()
	fmt.Println(cnt())
}
/*
输出结果:
call foo
call foo
call foo
call foo
4
*/
---
//加法器
func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	myAdder := adder()
	// 从1加到10
	for i := 1; i <= 10; i++ {
		myAdder(i)
	}
	fmt.Println(myAdder(0))
	// 再加上45
	fmt.Println(myAdder(45))
}
---

//斐波那契闭包处理
func fibonacci() func() int {
	b0 := 0
	b1 := 1
	return func() int {
		tmp := b0 + b1
		b0 = b1
		b1 = tmp
		return b1
	}

}

func main() {
	myFibonacci := fibonacci()
	for i := 1; i <= 5; i++ {
		fmt.Println(myFibonacci())
	}
}

3、装饰函数:函数是Go语言的一等公民,可以作为函数的参数进行传递。装饰器指的就是函数作为参数进行传递的情况。用于构造函数中对对象的封装和处理(尤其是成员较多的结构体)。闭包函数中本身是没有定义变量,而是引用了它所在的环境中的变量。

// Options 调用参数  被装饰的结构体
type Options struct {
  ...
  Discovery            discovery.Discovery
  LoadBalance          loadbalance.LoadBalancer
  CircuitBreaker       circuitbreaker.CircuitBreaker
}

// Option 调用参数工具函数
type Option func(*Options)


// WithDiscovery 指定服务发现
func WithDiscovery(d discovery.Discovery) Option {

  return func(o *Options) {
  	o.Discovery = d
  }
}
...//其它类似的装饰函数省略。。。。

// selector默认实现,内部自动串好 服务发现 负载均衡 熔断隔离 等流程
type Selector struct{}
// Select 输入service name,返回一个可用的node
func (s *Selector) Select(serviceName string, opt ...Option) (*registry.Node, error) {
  ...
  opts := &Options{
  	Discovery:      discovery.DefaultDiscovery,
  	LoadBalance:    loadbalance.DefaultLoadBalancer,
  	CircuitBreaker: circuitbreaker.DefaultCircuitBreaker,
  }
  for _, o := range opt {
  	o(opts)
  }
  ....
}

//newSelector
func newSelector(registry  discovery.DefaultDiscovery)*registry.Node {
  selector := &Selector{}
  n, err := selector.Select("configServer",WithDiscovery(registry ))
  ....
  return n
}

4、闭包引用的上下文变量会延长生命期,避免再次传递。

//普通函数:传递根据哪个后缀判断,其次是文件名字
func makeSuffix (suffix string, name string) string {
    if !strings.HasSuffix(name, suffix) {
        return name + suffix  //如果没有后缀就拼接
    }
    return name

} 
func main(){
    fmt.Println("文件名处理后:", makeSuffix("jpg","go语言圣经"))  
    fmt.Println("文件名处理后:", makeSuffix("jpg","PHP设计模式.jpg"))
}
---
//闭包函数,生成函数可以传入一个文件名,如果该文件名没有指定的后缀(如.jpg),则返回.jpg,如果有则全称
func makeSuffix (suffix string) func (string) string {
    return func (name string) string {
        if !strings.HasSuffix(name, suffix) {
            return name + suffix  //如果没有后缀就拼接
        }
        return name
    }
} 
func main(){
    //先返回一个闭包
    test := makeSuffix(".jpg")
    fmt.Println("文件名处理后:", test("go语言圣经"))  
    fmt.Println("文件名处理后:", test("PHP设计模式.jpg"))

}

5、内嵌匿名函数的并发调用,原因代码逻辑有for循环或处理时间较长的逻辑需要匿名函数包住另起协程运行,从而不阻塞影响后续逻辑。或者有defer等调用次序的问题需要在匿名函数中先调用defer等。

func main() {
    ch := make(chan struct{})
    go func() {
        fmt.Println("do something..")
        for {
        	 // process
		}
        time.Sleep(time.Second * 1)
        ch <- struct{}{}
    }()

    <-ch
    fmt.Println("I am finished")
}

6.拦截器的使用之中间件模式

多个中间件会形成一个栈结构(middle stack),以"先进后出"(first-in-last-out)的顺序执行,被称为洋葱结构。
在这里插入图片描述

如洋葱模型所示:调用请求先被第一个filter处理,然后依次交给后续的filter,最后交给业务逻辑处理。处理完之后又从内层的filter一层一层向外层返回,最后返回给客户端。请求先一层一层地进入洋葱,再一层一层地出来,这两个地方都可以写逻辑,也就是所谓的hook。先执行fitler函数,其中最后执行的是业务逻辑函数。

Handle的logic函数其实就是洋葱的中心——业务逻辑,这个在调用Handle时由框架设置好。然后Handle内部是一个递归调用的闭包,如果所有filter都执行完了,就执行logic函数,如果后面还有filter就执行filter.

// Chain 链式过滤器
type Chain []Filter

// Handle 链式过滤器递归处理流程
func (fc Chain) Handle(ctx context.Context, req interface{}, rsp interface{}, logic HandleFunc) (err error) {

	n := len(fc)
	curI := -1
	var chainFunc HandleFunc
	chainFunc = func(ctx context.Context, req interface{}, rsp interface{}) error {
		if curI == n-1 {
			return logic(ctx, req, rsp)
		}
		curI++
		err := fc[curI](ctx, req, rsp, chainFunc)
		return err
	}

	return chainFunc(ctx, req, rsp)
}