Go 装饰器模式与洋葱模型
前言 装饰器模式 是实现功能解耦和动态扩展的核心设计模式,而 洋葱模型 作为装饰器模式的链式进阶,是 Web 框架中间件、RPC 拦截器的底层核心。 本文介绍用 Go 实现支持 context.Context 上下文传递的装饰器模式(包括函数式和接口式两种实现),并进阶实现洋葱模型 一、装饰器模式 模式定义 装饰器模式是一种结构型设计模式,核心原则:不修改原有代码逻辑,动态为对象/函数添加额外功能。 它遵循开放封闭原则:对扩展开放,对修改关闭。 核心特性 动态增强:运行期为目标添加前置/后置逻辑 无侵入性:不改动核心业务代码 可组合:多个装饰器链式叠加,自由组合功能 上下文透传:配合 context.Context 实现全链路数据传递(超时、请求ID、用户信息等) 适用场景 日志打印、耗时统计 权限校验、参数校验 缓存、重试、限流熔断 全链路上下文管理 二、Go 实现装饰器模式(带 Context 上下文) 函数式装饰器 Go 没有类继承,通过函数包装实现装饰器是最简洁的方案。 为装饰器加入 context.Context,实现上下文全链路传递(核心需求)。 设计思路 定义统一的业务函数类型(强制携带 Context) 编写核心业务函数(无任何增强逻辑) 编写装饰器函数:接收原函数 → 返回包装后的新函数 装饰器内部通过 Context 传递/读取数据,实现全链路交互 函数式装饰器代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package main import ( "context" "fmt" "time" ) // 定义业务函数类型:携带 context.Context,支持上下文传递 // 这是装饰器的统一接口 type BusinessFunc func(ctx context.Context, name string) string // 原始核心业务函数:仅实现核心逻辑,无增强代码 func SayHello(ctx context.Context, name string) string { // 从上下文读取中间件传递的数据 reqID, _ := ctx.Value("req_id").(string) user, _ := ctx.Value("user").(string) fmt.Printf("[核心业务] 请求ID:%s | 操作用户:%s | 执行核心逻辑\n", reqID, user) return fmt.Sprintf("你好,%s!", name) } // 装饰器1:日志装饰器(携带上下文) func LogDecorator(f BusinessFunc) BusinessFunc { return func(ctx context.Context, name string) string { // 前置增强:打印请求日志 reqID := ctx.Value("req_id").(string) fmt.Printf("[日志装饰器] 请求ID:%s | 开始调用,参数:%s\n", reqID, name) // 调用原函数(透传上下文) result := f(ctx, name) // 后置增强:打印响应日志 fmt.Printf("[日志装饰器] 请求ID:%s | 调用结束,结果:%s\n", reqID, result) return result } } // 装饰器2:计时装饰器(携带上下文) func TimeDecorator(f BusinessFunc) BusinessFunc { return func(ctx context.Context, name string) string { start := time.Now() reqID := ctx.Value("req_id").(string) // 调用原函数(透传上下文) result := f(ctx, name) // 后置增强:打印耗时 fmt.Printf("[计时装饰器] 请求ID:%s | 执行耗时:%s\n", reqID, time.Since(start)) return result } } // 装饰器3:上下文初始化装饰器(往ctx存入数据,供下游使用) func ContextDecorator(f BusinessFunc) BusinessFunc { return func(ctx context.Context, name string) string { // 给上下文添加请求ID、用户信息(全链路透传) ctx = context.WithValue(ctx, "req_id", "REQ_123456") ctx = context.WithValue(ctx, "user", "admin") fmt.Println("[上下文装饰器] 初始化上下文完成") return f(ctx, name) // 无后置增强 } } func main() { // 链式装饰:顺序 = 上下文装饰 → 日志装饰 → 计时装饰 decoratedFunc := ContextDecorator(LogDecorator(TimeDecorator(SayHello))) // 根上下文 ctx := context.Background() // 执行增强后的函数 res := decoratedFunc(ctx, "张三") fmt.Println("\n最终返回结果:", res) } 运行结果 注意每层装饰器中被装饰函数 BusinessFunc 的调用位置,后面的代码是被装饰函数返回后再执行的 ...