高阶函数
所谓高阶函数,就是接收其他函数作为参数传入,或者把其他函数作为结果返回的函数。
装饰器模式
装饰器模式(Decorator)是一种软件设计模式,其应用场景是为某个已经存在的功能模块(类或者函数)添加一些【装饰】功能,而又不会侵入和修改原有的功能模块。就好比我们给房间做节日装饰一样,它不会调整这个房间原有的任何固有框架,而又让房间充满节日气氛。
有过 Python、Java 编程经验的同学应该对这个模式很熟悉,在 Python、Java 中,我们可以通过注解非常优雅地实现装饰器模式,比如给某个功能模块添加日志功能、或者为路由处理器添加中间件功能,这些都可以通过装饰器实现。
有过 Python、Java 编程经验的同学应该对这个模式很熟悉,在 Python、Java 中,我们可以通过注解非常优雅地实现装饰器模式,比如给某个功能模块添加日志功能、或者为路由处理器添加中间件功能,这些都可以通过装饰器实现。
不过 Go 语言的设计哲学就是简单,没有提供「注解」之类的语法糖,在函数式编程中,要实现装饰器模式,可以借助高阶函数来实现。
通过高阶函数实现装饰器模式
接下来,我们以一个乘法运算函数为例,来演示如何在 Go 语言中通过高阶函数来实现装饰器模式。
先编写这个乘法运算函数和调用代码:
package main
import "fmt"
func multiply(a, b int) int {
return a * b
}
func main() {
a := 2
b := 8
c := multiply(a, b)
fmt.Printf("%d x %d = %d\n", a, b, c)
}
运行上述代码,打印结果如下:
2 x 8 = 16
现在,我们想要在不修改现有 multiply
函数代码的前提下计算乘法运算的执行时间,显然,这可以引入装饰器模式来实现。
装饰器模式实现
我们已经说过,Go 不支持注解,需要借助高阶函数来实现这个计算耗时的装饰器,对应实现代码如下:
package main
import (
"fmt"
"time"
)
// 为函数类型设置别名提高代码可读性
type MultiPlyFunc func(int,int)int
//乘法运算函数
func multiply(a,b int)int{
return a * b
}
//通过高阶函数在不侵入原有函数实现的前提下计算乘法函数执行时间
func execTime(f MultiPlyFunc) MultiPlyFunc{
return func(a int, b int) int {
start := time.Now() //起始时间
c := f(a,b)//执行乘法运算函数
end := time.Since(start)
fmt.Println("---执行耗时:---",end)
return c
}
}
func main() {
a :=2
b:=8
//通过修饰器调用乘法函数,返回的是一个匿名函数
decorator := execTime(multiply)
//执行修饰器返回函数
c := decorator(a,b)
fmt.Printf("%d * %d = %d\n",a,b,c)
}
分析一下代码,首先,我们通过type
语句为匿名函数类型设置了别名MultiPlyFunc
,这样一来,后续就可以用这个类型别名来声明对应的函数类型参数和返回值,提高代码可读性。
然后是装饰器模式实现代码execTime
函数,这是一个以MultiPlyFunc
类型为参数和返回值的函数,所以是个高阶函数,我们看下具体实现代码:
- 在返回的
MultiPlyFunc
类型匿名函数体中,真正执行乘法运算函数f
前,先通过time.Now()
获取当前系统时间,并将返回值赋值给变量c
; - 然后执行
f
函数,将返回赋值给变量c
; - 接下来,通过
time.Since(start)
计算从start
到现在经过的时间,也就是f
函数执行耗时,将结果赋值给end
变量并打印出来; - 最后返回
f
函数运行结果c
作为最终返回值
核心思路:在被修饰的功能模块(这里是外部传入的乘法函数
f
)执行前后加上一些额外的业务逻辑,而又不影响原有功能模块的执行。显然,装饰器模式是遵循SQLID设计原则中的开放封闭原则的—对代码扩展开放,对代码修改关闭
在
main
函数中调用乘法函数multiply
时,如果要应用装饰器,需要通过装饰器execTime
包裹,装饰器返回的是一个匿名函数,所以需要再度调用才能真正执行,执行后的打印结果如下:
---执行耗时:--- 0s
2 * 8 = 16
通过装饰器模式,只需要为基础修饰功能写一次代码,后面新的业务逻辑只需要维护新增部分即可,不需要侵入原有功能模块,让代码的可维护性和可读性更好。