如何用Go的reflect.MakeFunc实现可配置时机的函数装饰?
基于Go Reflect实现可配置执行时机的函数装饰器
要实现支持前置/后置注入逻辑的装饰器,需要调整现有代码结构,让Decorate能接收执行时机和自定义处理函数,同时让Handle具备处理原函数参数、返回值的能力。以下是完整实现:
核心调整点
- 定义执行时机枚举,明确区分前置/后置逻辑
- 重构处理函数的签名,使其能接收原函数参数、原返回值,并输出修改后的结果
- 扩展
Decorate函数,接收时机配置和处理函数,动态生成装饰后的函数
完整代码
package main import ( "errors" "fmt" "reflect" ) // 定义执行时机枚举 type ExecuteTiming int const ( Before ExecuteTiming = iota // 前置执行:先运行处理逻辑,再决定是否执行原函数 After // 后置执行:先运行原函数,再用处理逻辑修改返回结果 ) // 定义处理函数的统一类型,兼容原函数的参数和返回值处理 type HandleFunc func(args []reflect.Value, originalResults []reflect.Value) []reflect.Value func main() { func1 := OriginFunc // 示例1:前置注入错误,直接返回错误不执行原函数 Decorate(&func1, Before, func(args []reflect.Value, originalResults []reflect.Value) []reflect.Value { // 构造符合原函数返回类型的结果:int返回0,error返回自定义错误 return []reflect.Value{ reflect.ValueOf(0), reflect.ValueOf(errors.New("前置注入错误")), } }) val, err := func1(1, 2) fmt.Printf("前置注入结果:val=%d, err=%v\n", val, err) // 重置原函数,测试后置注入 func1 = OriginFunc Decorate(&func1, After, func(args []reflect.Value, originalResults []reflect.Value) []reflect.Value { // 保留原函数的计算结果,替换error为自定义错误 return []reflect.Value{ originalResults[0], reflect.ValueOf(errors.New("后置注入错误")), } }) val, err = func1(1, 2) fmt.Printf("后置注入结果:val=%d, err=%v\n", val, err) } func OriginFunc(a, b int) (int, error) { return a + b, nil } // Decorate 扩展为可配置执行时机和处理函数的装饰器 func Decorate(fn interface{}, timing ExecuteTiming, handle HandleFunc) { v := reflect.ValueOf(fn).Elem() originalFn := v m := reflect.MakeFunc(v.Type(), func(args []reflect.Value) []reflect.Value { switch timing { case Before: // 前置逻辑:直接调用处理函数,返回其生成的结果(原函数不会执行) return handle(args, nil) case After: // 后置逻辑:先执行原函数获取结果,再传给处理函数修改 originalResults := originalFn.Call(args) return handle(args, originalResults) default: // 默认直接执行原函数 return originalFn.Call(args) } }) v.Set(m) }
代码说明
- ExecuteTiming枚举:用常量明确两种执行时机,避免硬编码带来的维护问题
- HandleFunc类型:统一处理函数的签名,确保能适配不同原函数的参数和返回值结构
- Decorate函数扩展:通过reflect动态生成新函数,根据传入的时机参数控制执行顺序
- 示例场景:
- 前置注入:直接返回自定义错误,原函数逻辑不会触发
- 后置注入:保留原函数的业务计算结果,仅修改返回的错误信息
内容的提问来源于stack exchange,提问作者Pkzzz
相关产品推荐
相关产品推荐

