应根据扩展需求选择组合、装饰器或访问者模式:组合适用于增强已有类型行为(如为http.client加超时),装饰器适合接口的横切关注点(如日志、缓存),访问者用于多类型树形结构的新操作(如ast遍历)。

不能直接改第三方代码,但有 3 种安全、类型安全、可维护的扩展方式:组合(embedding)、装饰器(decorator)、访问者(visitor)——选哪种,取决于你要加的功能是否依赖结构体内部字段、是否需链式叠加、是否涉及多种类型遍历。
用组合(embedding)包装原类型,复用方法并添加新行为
这是最常用也最推荐的方式,适用于你想“在原有能力基础上加一点逻辑”,比如给 http.Client 加默认超时或日志,或给某个 SDK 客户端加重试封装。
- 必须定义一个新结构体,把第三方类型作为匿名字段嵌入
- 新结构体实现原接口(如果原类型实现了接口),或导出新方法调用原字段方法
- 注意:不能直接嵌入指针类型(如
*http.Client)再期望自动代理方法——Go 不会自动提升指针字段的方法集;应嵌入值类型或显式转发 - 示例中常见错误是忘记重写关键方法(如
Do()),导致新逻辑被绕过
用装饰器模式增强接口方法,适合横切关注点
当你面对的是一个接口(如 io.Reader、database/sql/driver.Driver、自定义的 Service 接口),且想加日志、监控、缓存等不侵入业务逻辑的功能时,装饰器最合适。
- 定义与原接口签名一致的新结构体,持有一个原接口实例
- 所有方法都先/后插入自定义逻辑,再调用底层实例对应方法
- 多个装饰器可嵌套使用,但要注意执行顺序:外层装饰器的
Before先执行,After后执行 - 避免在装饰器里做耗时同步操作(如网络请求),否则会阻塞整个调用链
用访问者模式处理多类型结构树,新增操作无需动原有类型
如果你拿到的是一个包含多种子类型的树形结构(比如 AST、文件系统模型、配置对象图),而你需要为每种类型分别实现新操作(如序列化、校验、统计),访问者模式比类型断言更清晰、更易维护。
- 第三方类型需实现统一的
Accept(v Visitor)方法——若它们没提供,你就得自己包装一层(即先用组合造个 wrapper,再在 wrapper 里实现Accept) -
Visitor接口要为每种子类型声明对应VisitXXX方法,新增功能只需实现该接口,不碰原结构 - 容易踩的坑是:当结构类型增加时,所有已有
Visitor实现都得补新方法,否则编译失败——这其实是优点,能强制发现遗漏 - 不适用于只有单一类型或类型关系极简的场景,会过度设计
真正难的不是选哪种模式,而是判断「这个扩展到底该属于数据本身,还是属于使用它的上下文」。前者倾向组合,后者倾向装饰器或访问者。很多团队一开始用反射或 map[string]func 硬塞逻辑,结果半年后没人敢改——静态类型 + 显式组合才是 Go 里最不容易翻车的扩展路径。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











