资讯动态

设计模式7——桥接模式(Bridge Pattern)

发布时间:2026/10/23 7:17:44 来源:尧图企业网站定制
1. 提出桥接模式前的痛点场景设想一个图形绘制系统系统中有多种形状Shape例如圆形Circle、方形Square同时有多种渲染方式Renderer例如 OpenGL、Vulkan、DirectX。如果直接用继承来建模最容易想到的方案是为「形状 × 渲染方式」的每一种组合都创建一个类Shape形状 ├── Circle圆形 │ ├── CircleOpenGL │ ├── CircleVulkan │ └── CircleDirectX └── Square方形 ├── SquareOpenGL ├── SquareVulkan └── SquareDirectX当系统规模较小时这种方式似乎可以接受。然而随着系统演进问题会集中爆发类的数量呈乘积式爆炸形状种类 M 乘以渲染方式 N就需要 M × N 个具体类。形状增加 1 种、渲染方式增加 1 种都需要成倍新增子类。新增一个维度极不灵活如果要再加入线条风格实线、虚线类的组合数量会进一步膨胀几乎无法维护。代码高度重复每个子类里都重复写形状计算逻辑和渲染调用逻辑违背单一职责原则。2. 模式基本思路桥接模式Bridge Pattern的核心思想是将抽象部分与实现部分分离使它们都可以独立地变化。具体做法是把继承关系改造为组合关系抽象部分Abstraction面向业务的高层概念比如「形状」。它持有一个指向实现部分的引用/指针。实现部分Implementation可被替换的底层实现比如「渲染方式」。它通常被定义成接口抽象基类。抽象部分通过组合而非继承调用实现部分二者之间的连接被称为「桥」。UML 结构如下桥接Shape-Renderer* rendererdraw()Circledraw()Squaredraw()Rendererrender()OpenGLRendererrender()VulkanRendererrender()这样一来「形状」和「渲染方式」两个维度可以各自独立扩展。例如新增一种渲染方式时只需新增一个 Renderer 子类所有形状都能立即使用它无需为每个形状重复派生子类。3. C 案例图形绘制系统下面用一个完整可运行的 C 案例演示桥接模式。抽象部分为Shape形状实现部分为Renderer渲染器通过指针完成桥接。3.1 实现部分渲染器接口// renderer.h#pragmaonce#includeiostream#includestring// 渲染器接口classRenderer{public:virtual~Renderer()default;virtualvoidrender(conststd::stringshapeName)0;};// 具体OpenGL 渲染器classOpenGLRenderer:publicRenderer{public:voidrender(conststd::stringshapeName)override{std::cout使用 OpenGL 绘制 shapeNamestd::endl;}};// 具体Vulkan 渲染器classVulkanRenderer:publicRenderer{public:voidrender(conststd::stringshapeName)override{std::cout使用 Vulkan 绘制 shapeNamestd::endl;}};3.2 抽象部分形状基类// shape.h#pragmaonce#includememory#includestring#includerenderer.h// 抽象部分形状基类持有一个渲染器指针作为“桥”classShape{public:explicitShape(std::shared_ptrRendererrenderer):renderer_(std::move(renderer)){}virtual~Shape()default;virtualvoiddraw()0;protected:std::shared_ptrRendererrenderer_;};// 扩展抽象圆形classCircle:publicShape{public:explicitCircle(std::shared_ptrRendererrenderer):Shape(std::move(renderer)){}voiddraw()override{renderer_-render(圆形);}};// 扩展抽象方形classSquare:publicShape{public:explicitSquare(std::shared_ptrRendererrenderer):Shape(std::move(renderer)){}voiddraw()override{renderer_-render(方形);}};3.3 客户端调用// main.cpp#includeshape.h#includerenderer.hintmain(){// 创建不同的实现渲染器autoopenglstd::make_sharedOpenGLRenderer();autovulkanstd::make_sharedVulkanRenderer();// 组合抽象与实现两个维度独立变化CirclecircleWithOpenGL(opengl);SquaresquareWithVulkan(vulkan);CirclecircleWithVulkan(vulkan);// 新增组合无需新增子类circleWithOpenGL.draw();squareWithVulkan.draw();circleWithVulkan.draw();return0;}运行结果使用 OpenGL 绘制 圆形 使用 Vulkan 绘制 方形 使用 Vulkan 绘制 圆形3.4 扩展验证新增渲染方式与新增线条风格维度均零负担先验证同维度扩展。假设现在要新增一种渲染方式DirectXRenderer只需新增一个Renderer子类classDirectXRenderer:publicRenderer{public:voidrender(conststd::stringshapeName)override{std::cout使用 DirectX 绘制 shapeNamestd::endl;}};所有形状Circle、Square无需任何改动即可搭配新渲染器使用。同理新增一种形状也只需派生一个Shape子类完全不影响渲染器体系。新增一个独立的维度若继续用继承类数量会变成「形状 × 渲染方式 × 线条风格」的乘积爆炸桥接模式则继续用组合拆解。// line_style.h#pragmaonce#includeiostream#includestring// 线条风格接口classLineStyle{public:virtual~LineStyle()default;virtualvoidapply(conststd::stringshapeName)0;};// 具体线条风格实线classSolidLineStyle:publicLineStyle{public:voidapply(conststd::stringshapeName)override{std::cout使用实线风格绘制 shapeNamestd::endl;}};// 具体线条风格虚线classDashedLineStyle:publicLineStyle{public:voidapply(conststd::stringshapeName)override{std::cout使用虚线风格绘制 shapeNamestd::endl;}};然后在实现侧增加一个StyledRenderer它本身仍然是Renderer只是内部再组合「真正的渲染器」和「线条风格」// styled_renderer.h#pragmaonce#includememory#includerenderer.h#includeline_style.h// 实现侧扩展为一个渲染器附加线条风格classStyledRenderer:publicRenderer{public:StyledRenderer(std::shared_ptrRendererrenderer,std::shared_ptrLineStylestyle):renderer_(std::move(renderer)),style_(std::move(style)){}voidrender(conststd::stringshapeName)override{renderer_-render(shapeName);style_-apply(shapeName);}private:std::shared_ptrRendererrenderer_;std::shared_ptrLineStylestyle_;};此时抽象侧Shape、Circle、Square保持 3.2 中的原样构造参数仍然只有一个Renderer完全不用改动。客户端按需组合// main.cppautoopenglstd::make_sharedOpenGLRenderer();autosolidstd::make_sharedSolidLineStyle();//实线autodashedstd::make_sharedDashedLineStyle();//虚线// 把「渲染方式 线条风格」组合成新的 RendererautoopenglSolidstd::make_sharedStyledRenderer(opengl,solid);autoopenglDashedstd::make_sharedStyledRenderer(opengl,dashed);// 抽象侧接口不变仍然只接收 RendererCirclec1(openglSolid);Circlec2(openglDashed);Squares1(openglDashed);c1.draw();c2.draw();s1.draw();运行结果使用 OpenGL 绘制 圆形 使用实线风格绘制 圆形 使用 OpenGL 绘制 圆形 使用虚线风格绘制 圆形 使用 OpenGL 绘制 方形 使用虚线风格绘制 方形可以看到新增「线条风格」维度时抽象部分接口零修改只在实现侧新增LineStyle接口与StyledRenderer组合类。形状、渲染方式、线条风格仍然自由组合却没有为每个组合新建子类类规模保持加法级增长同时业务抽象Shape保持稳定。这才是桥接模式「独立变化、按需组合」的关键。4. 小结桥接模式通过「组合代替继承」成功解耦了抽象与实现两个独立变化的维度从根本上避免了子类组合爆炸。在 C 中通常借助抽象基类、虚函数以及智能指针实现这一桥接。当你的系统中存在两个或多个独立变化维度且使用继承会导致类数量乘积增长时就是引入桥接模式的合适时机。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑