桥接模式在c#中通过组合+接口解耦实现,而非继承;适用于抽象与实现需独立变化的场景,如图形类型与渲染方式双维度扩展,新增类互不影响,避免子类爆炸。

桥接模式在 C# 中不是靠继承堆出来的,而是靠组合 + 接口解耦实现的;用错地方反而让代码更难懂,别一上来就套模板。
什么时候该用 Bridge 而不是继承
当你发现类要同时沿着两个方向扩展——比如「图形类型(圆形、矩形)」和「渲染方式(矢量、光栅、WebGL)」——这时候每加一个图形 × 每加一种渲染,继承就会爆炸式增长。用桥接,新增图形只需写一个 Shape 子类,新增渲染只需写一个 IRenderer 实现,互不干扰。
- 抽象部分变化频繁(如支持新图形)
- 实现部分也常变(如适配新平台、新 API)
- 你已经在写类似
CircleWindowsRenderer、CircleLinuxRenderer这种名字的类了 → 就是信号 - 运行时需要切换实现(比如用户点个按钮从光栅切到矢量)→ 桥接天然支持
SetRenderer(new VectorRenderer())
Abstraction 类里为什么必须持有一个 Implementor 接口引用
这是桥接的“桥”本身。没有这个引用,抽象就只能自己硬编码实现逻辑,又回到紧耦合。接口类型(比如 IDrawingAPI 或 IRenderer)保证了抽象层完全不知道具体实现细节。
- 构造函数传入,不是 new 出来:避免抽象类依赖具体实现
- 字段声明为接口,不是具体类:
private readonly IRenderer _renderer;,不是VectorRenderer - 如果用了
new VectorRenderer()在抽象类里,桥就断了,模式失效 - 推荐用
protected字段或readonly属性暴露给子类,但绝不 public set
RefinedAbstraction 和 ConcreteImplementor 怎么分工才不乱
RefinedAbstraction(比如 Circle)只负责「做什么」:描述图形语义(位置、半径)、调用 _renderer.Render("circle");ConcreteImplementor(比如 WebGLRenderer)只负责「怎么做」:真正调 OpenGL/WebGL API 画圆。
-
Circle.Draw()不写任何绘图代码,只做参数组装和委托调用 -
WebGLRenderer.Render()不知道 Circle 是啥,只接收字符串或结构体参数 - 如果
Circle开始检查_renderer is WebGLRenderer做分支逻辑 → 桥接退化为条件判断,耦合回潮 - 参数尽量扁平(
string shapeName或轻量ShapeData),避免把抽象层的数据模型强塞进实现层
容易被忽略的坑:生命周期与线程安全
桥接模式本身不解决资源管理问题。如果你的 IRenderer 持有 OpenGL 上下文或文件句柄,而 Shape 实例被反复复用、跨线程传递,就可能出错。
-
IRenderer实现类如果是有状态的(如缓存着色器程序),需自行保证线程安全或明确文档标注「非线程安全」 - 不要让多个
Shape共享同一个可变IRenderer实例,除非你确认它设计为可重入 - 常见反模式:
Shape构造时 new 一个Renderer,又没 Dispose 机制 → 内存泄漏或上下文丢失 - 建议:
IRenderer实现IDisposable,由上层容器(如RenderingEngine)统一管理其生命周期,而非交给每个Shape










