Go中桥接模式通过组合接口实现抽象与实现解耦,核心是结构体持有接口字段并运行时注入具体实现,而非继承类图。

桥接模式在 Go 里不靠继承,而靠组合 + 接口,核心是把“抽象”和“实现”解耦,让两者能独立变化。
为什么 Go 不适合用传统桥接模式类图实现
Go 没有类、没有继承、也没有 abstract class。强行模仿 UML 类图(比如定义 AbstractImplementor、RefinedAbstraction)只会让代码变重、失去 Go 的简洁性。真正的桥接,在 Go 里就是:一个结构体持有接口类型字段,运行时注入具体实现。
-
Abstraction是普通 struct,内嵌或持有Implementor接口变量 -
Implementor是接口,定义底层能力契约(如Draw()、Connect()) - 具体实现(如
OpenGLRenderer、PostgreSQLDriver)只管实现该接口,不和上层抽象耦合 - 调用方创建
Abstraction{impl: &MySQLDriver{}}即完成桥接,无需工厂或复杂初始化
典型场景:图形渲染器与平台适配分离
比如你要支持不同图形 API(OpenGL/Vulkan)在不同操作系统(Windows/macOS/Linux)上渲染——API 是实现维度,OS 是平台维度。桥接能让你自由组合,而不是写 OpenGLWindowsRenderer、VulkanLinuxRenderer 等爆炸式类。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
《PHP设计模式》首先介绍了设计模式,讲述了设计模式的使用及重要性,并且详细说明了应用设计模式的场合。接下来,本书通过代码示例介绍了许多设计模式。最后,本书通过全面深入的案例分析说明了如何使用设计模式来计划新的应用程序,如何采用PHP语言编写这些模式,以及如何使用书中介绍的设计模式修正和重构已有的代码块。作者采用专业的、便于使用的格式来介绍相关的概念,自学成才的编程人员与经过更多正规培训的编程人员
- 定义
Renderer接口(含RenderTriangle()、SetViewport()) - 实现
OpenGLRenderer和VulkanRenderer,都满足Renderer - 定义
GraphicsEngine结构体,字段为renderer Renderer - 使用时:
eng := &GraphicsEngine{renderer: &VulkanRenderer{}}—— 切换实现只需换构造参数
注意:不要在 GraphicsEngine 里暴露 renderer 字段为 public;如果需要动态切换,提供 SetRenderer(r Renderer) 方法即可。
常见错误:把桥接写成策略模式或依赖注入容器
桥接的关键是「抽象层级稳定,实现可替换且可能多维变化」。如果只是根据不同条件选一种算法(比如压缩用 gzip 还是 zstd),那是策略模式;如果用 dig 或 wire 自动注入所有依赖,那属于 DI 范畴,反而模糊了桥接的意图。
- 错误信号:
Abstraction中大量方法只是透传给Implementor,且没有自己的状态或逻辑 —— 这说明你可能只需要直接用接口,不需要桥接 - 错误信号:
Implementor接口方法频繁增删,或每个实现只被一个Abstraction使用 —— 桥接带来的解耦收益为零 - 正确信号:
Abstraction有自己的生命周期管理(如初始化/销毁资源)、缓存、上下文封装,而Implementor专注纯能力交付
性能与兼容性提醒:接口非零成本,但通常可忽略
Go 接口调用有微小开销(iface 查表),但在绝大多数桥接场景(如数据库驱动、日志后端、网络传输层),这远小于 I/O 或计算本身。真正要注意的是内存逃逸和指针间接访问:
- 避免在 hot path 上高频新建接口值(如循环里
doSomething(Renderer)传临时 struct) - 若
Implementor实现是小结构体(如type NullLogger struct{}),考虑用值接收而非指针接收,减少堆分配 - Go 1.20+ 支持接口方法内联优化,但前提是实现方法足够简单且未被其他包重写 —— 所以尽量让
Implementor接口方法保持单一职责
桥接是否必要,取决于你是否预见到抽象层和实现层会沿着不同方向演进。如果现在只有一个实现,又没明确扩展计划,先用接口直连,比提前桥接更 Go-idiomatic。









