桥接模式通过组合解耦抽象与实现。1.核心是将“做什么”和“怎么做”分离,避免类爆炸;2.结构包含抽象、精化抽象、实现者、具体实现者四个角色;3.适用于多维度变化场景如跨平台ui或图形绘制;4.c++++中需注意实现者生命周期管理;5.区别于策略模式(行为切换)和适配器模式(接口转换),侧重结构解耦。

C++的桥接模式,说白了,就是把一个大问题拆成两个可以独立变化的小问题,让“做什么”(抽象)和“怎么做”(实现)这两条线能各走各的路,互不干扰。这就像一座桥,连接了两岸,但两岸上的风景怎么变,桥本身的功能和结构是相对独立的,它只负责连接。核心在于通过组合而非继承,将抽象层和实现层解耦,从而允许它们各自独立地扩展和演进。

我们在软件设计中,有时候会遇到这样的场景:你有一个概念,它有很多种变体,同时它又可以在多种不同的环境下运行或以多种方式实现。如果用传统的继承方式来处理,很快就会陷入一个“类爆炸”的泥潭。比如,你有一堆图形(圆形、方形、三角形),它们又要在不同的绘图API上绘制(OpenGL、DirectX、SVG)。如果直接用继承,你可能需要CircleOpenGL、CircleDirectX、SquareOpenGL、SquareDirectX……每增加一个图形或一个绘图API,类的数量就会呈乘法级增长,维护起来简直是噩梦。

桥接模式提供了一个优雅的解决方案。它将抽象(比如Shape)和实现(比如DrawingAPI)分离开来。抽象层定义了高层接口,它内部持有一个指向实现层接口的指针或引用。当抽象层需要执行某个操作时,它就把这个操作委托给它所持有的实现对象。这样一来,抽象的具体实现(比如Circle)就不再关心它具体是在哪个绘图API上绘制的,它只知道通过一个DrawingAPI接口去调用绘制方法。而DrawingAPI的具体实现(比如OpenGLAPI)也只负责它自己的绘图逻辑,它不关心是哪个形状在调用它。
立即学习“C++免费学习笔记(深入)”;
这种分离带来的好处是显而易见的:

说实话,C++里桥接模式的应用场景还挺多的,不只是图形绘制这么简单。我个人觉得,当你发现一个类或者一组类的继承体系,开始因为两个或更多个正交(也就是互相独立)的变化维度而变得臃肿不堪时,就应该考虑它了。
一个非常经典的例子就是跨平台的用户界面(UI)工具包。想象一下,你有一个Button抽象类,它在Windows上可能对应一个Win32Button,在macOS上对应一个CocoaButton,在Linux上可能对应一个GTKButton。Button的“行为”(点击、禁用等)是一个维度,而它在不同操作系统上的“具体绘制和事件处理”(实现)是另一个维度。如果直接继承,你就会有WindowsButton、MacButton、LinuxButton,然后你再来个WindowsCheckbox、MacCheckbox……这会很糟糕。
用桥接模式,你可以定义一个UIElement(抽象)和PlatformImplementor(实现)接口。Button、Checkbox等继承自UIElement,它们内部持有PlatformImplementor的引用。而Win32Implementor、CocoaImplementor、GTKImplementor则实现PlatformImplementor接口。这样,你新增一个UI控件(比如Slider),只需要继承UIElement,而不需要关心平台细节;新增一个平台支持,只需要实现PlatformImplementor接口,而不需要修改所有UI控件的逻辑。
另一个常见但可能不那么显眼的场景是,当你需要隐藏一个类的具体实现细节,只暴露一个稳定的接口给客户端时,也就是所谓的PIMPL(Pointer to IMPLementation)惯用法。PIMPL本质上就是桥接模式的一种特例。你的公共类(抽象)只包含一个指向私有实现类的指针,所有实际的业务逻辑都在私有实现类中。这不仅可以减少头文件依赖,加快编译速度,还能在不破坏ABI兼容性的前提下修改内部实现。
在C++中实现桥接模式,主要涉及四个核心角色:抽象(Abstraction)、精化抽象(Refined Abstraction)、实现者(Implementor)和具体实现者(Concrete Implementor)。理解它们各自的职责以及如何用C++的特性来表达,是关键。
抽象(Abstraction):
这是客户端代码直接打交道的接口。它通常是一个抽象基类,定义了高层操作。它内部会持有一个指向Implementor接口的指针或引用。
// 抽象基类
class DrawingAPI { // Implementor 接口
public:
virtual void drawCircle(double x, double y, double radius) = 0;
virtual ~DrawingAPI() = default;
};
// 抽象
class Shape {
protected:
DrawingAPI* drawingAPI_; // 持有实现者的引用
public:
Shape(DrawingAPI* api) : drawingAPI_(api) {}
virtual void draw() = 0; // 高层操作
virtual ~Shape() = default;
};这里Shape是抽象,它依赖于DrawingAPI这个实现者接口。
精化抽象(Refined Abstraction):
这是Abstraction的具体子类,它实现了Abstraction定义的高层操作。这些操作通常会通过委托调用Implementor的方法。
// 精化抽象:圆形
class Circle : public Shape {
private:
double x_, y_, radius_;
public:
Circle(double x, double y, double r, DrawingAPI* api)
: Shape(api), x_(x), y_(y), radius_(r) {}
void draw() override {
// 将具体绘制操作委托给 drawingAPI_
drawingAPI_->drawCircle(x_, y_, radius_);
}
};Circle就是Shape的精化抽象,它通过其内部的drawingAPI_来完成绘制。
实现者(Implementor): 这是一个接口(在C++中通常是纯虚基类),定义了抽象层所需的所有基本操作。它不关心这些操作具体是如何被实现的,只提供一个规范。
// DrawingAPI 就是 Implementor 接口
// class DrawingAPI { ... }; (已在上方定义)具体实现者(Concrete Implementor):
这是Implementor接口的具体实现。每个具体实现者都提供了一套不同的方式来实现Implementor定义的操作。
// 具体实现者:OpenGL 绘图API
class OpenGLDrawingAPI : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) override {
std::cout << "Drawing Circle with OpenGL at (" << x << "," << y << ") radius " << radius << std::endl;
// 实际的OpenGL调用...
}
};
// 具体实现者:DirectX 绘图API
class DirectXDrawingAPI : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) override {
std::cout << "Drawing Circle with DirectX at (" << x << "," << y << ") radius " << radius << std::endl;
// 实际的DirectX调用...
}
};在实际使用时,客户端代码会创建具体的Implementor对象,然后将其传递给Abstraction的构造函数。
// 客户端代码
// #include <iostream>
// #include <memory> // For std::unique_ptr
// ... (类定义) ...
int main() {
// 创建具体的实现者
std::unique_ptr<DrawingAPI> openglAPI = std::make_unique<OpenGLDrawingAPI>();
std::unique_ptr<DrawingAPI> directxAPI = std::make_unique<DirectXDrawingAPI>();
// 创建抽象,并传入不同的实现者
std::unique_ptr<Shape> circleOpenGL = std::make_unique<Circle>(1.0, 2.0, 3.0, openglAPI.get());
std::unique_ptr<Shape> circleDirectX = std::make_unique<Circle>(5.0, 6.0, 7.0, directxAPI.get());
circleOpenGL->draw(); // 输出: Drawing Circle with OpenGL...
circleDirectX->draw(); // 输出: Drawing Circle with DirectX...
// 运行时切换实现(如果抽象允许)
// 比如,你可以在一个工厂方法中根据配置返回不同的 DrawingAPI 实例
// 或者 Shape 内部提供一个 setDrawingAPI 方法
// 但通常来说,Bridge模式的连接是在对象创建时建立的。
return 0;
}需要注意的是,在C++中处理Implementor的生命周期是个关键点。上面例子中我用了原始指针,但实际项目中,为了避免内存泄漏和管理复杂性,通常会使用智能指针(如std::unique_ptr或std::shared_ptr)来管理DrawingAPI对象的生命周期。如果一个DrawingAPI实例会被多个Shape对象共享,那么std::shared_ptr会是更好的选择。
这几个设计模式确实在结构上有些相似之处,都涉及到了“委托”或者“封装”,但它们解决的问题和侧重点是不同的。理解它们的异同,能帮助我们更准确地选择合适的模式。
与策略模式(Strategy Pattern)的异同:
与适配器模式(Adapter Pattern)的异同:
LegacyLogger接口,但你的新系统只认识NewLogger接口,你需要一个LegacyLoggerAdapter来把LegacyLogger包装成NewLogger。总的来说,桥接模式是当你预见到系统将会有两个或更多个独立变化的维度时,用来解耦和避免类爆炸的利器。它让你的设计更加灵活,更易于维护和扩展。当然,引入桥接模式会增加一些类的数量和间接性,所以并非所有简单场景都适用,它更适合那些复杂度较高、变化频繁的系统。
以上就是C++桥接模式如何分离抽象 实现独立变化的两个维度设计的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号