0

0

C++桥接模式如何分离抽象 实现独立变化的两个维度设计

P粉602998670

P粉602998670

发布时间:2025-07-11 09:55:01

|

228人浏览过

|

来源于php中文网

原创

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

C++桥接模式如何分离抽象 实现独立变化的两个维度设计

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

C++桥接模式如何分离抽象 实现独立变化的两个维度设计

解决方案

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

C++桥接模式如何分离抽象 实现独立变化的两个维度设计

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

立即学习C++免费学习笔记(深入)”;

这种分离带来的好处是显而易见的:

C++桥接模式如何分离抽象 实现独立变化的两个维度设计
  1. 独立变化:你可以增加新的形状,而无需修改任何绘图API的实现;同样,你可以增加新的绘图API,而无需修改任何形状的实现。
  2. 减少耦合:抽象和实现之间的耦合度大大降低,它们通过一个接口松散地联系在一起。
  3. 运行时切换:你甚至可以在运行时切换抽象所使用的具体实现,比如在不同的绘图模式之间切换。

C++中桥接模式的实际应用场景有哪些?

说实话,C++里桥接模式的应用场景还挺多的,不只是图形绘制这么简单。我个人觉得,当你发现一个类或者一组类的继承体系,开始因为两个或更多个正交(也就是互相独立)的变化维度而变得臃肿不堪时,就应该考虑它了。

一个非常经典的例子就是跨平台的用户界面(UI)工具包。想象一下,你有一个Button抽象类,它在Windows上可能对应一个Win32Button,在macOS上对应一个CocoaButton,在Linux上可能对应一个GTKButtonButton的“行为”(点击、禁用等)是一个维度,而它在不同操作系统上的“具体绘制和事件处理”(实现)是另一个维度。如果直接继承,你就会有WindowsButtonMacButtonLinuxButton,然后你再来个WindowsCheckboxMacCheckbox……这会很糟糕。

用桥接模式,你可以定义一个UIElement(抽象)和PlatformImplementor(实现)接口。ButtonCheckbox等继承自UIElement,它们内部持有PlatformImplementor的引用。而Win32ImplementorCocoaImplementorGTKImplementor则实现PlatformImplementor接口。这样,你新增一个UI控件(比如Slider),只需要继承UIElement,而不需要关心平台细节;新增一个平台支持,只需要实现PlatformImplementor接口,而不需要修改所有UI控件的逻辑。

另一个常见但可能不那么显眼的场景是,当你需要隐藏一个类的具体实现细节,只暴露一个稳定的接口给客户端时,也就是所谓的PIMPL(Pointer to IMPLementation)惯用法。PIMPL本质上就是桥接模式的一种特例。你的公共类(抽象)只包含一个指向私有实现类的指针,所有实际的业务逻辑都在私有实现类中。这不仅可以减少头文件依赖,加快编译速度,还能在不破坏ABI兼容性的前提下修改内部实现。

你好星识
你好星识

你的全能AI工作空间

下载

如何在C++中构建桥接模式的各个组成部分?

在C++中实现桥接模式,主要涉及四个核心角色:抽象(Abstraction)、精化抽象(Refined Abstraction)、实现者(Implementor)和具体实现者(Concrete Implementor)。理解它们各自的职责以及如何用C++的特性来表达,是关键。

  1. 抽象(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这个实现者接口。

  2. 精化抽象(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_来完成绘制。

  3. 实现者(Implementor): 这是一个接口(在C++中通常是纯虚基类),定义了抽象层所需的所有基本操作。它不关心这些操作具体是如何被实现的,只提供一个规范。

    // DrawingAPI 就是 Implementor 接口
    // class DrawingAPI { ... }; (已在上方定义)
  4. 具体实现者(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 
// #include  // For std::unique_ptr

// ... (类定义) ...

int main() {
    // 创建具体的实现者
    std::unique_ptr openglAPI = std::make_unique();
    std::unique_ptr directxAPI = std::make_unique();

    // 创建抽象,并传入不同的实现者
    std::unique_ptr circleOpenGL = std::make_unique(1.0, 2.0, 3.0, openglAPI.get());
    std::unique_ptr circleDirectX = std::make_unique(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_ptrstd::shared_ptr)来管理DrawingAPI对象的生命周期。如果一个DrawingAPI实例会被多个Shape对象共享,那么std::shared_ptr会是更好的选择。

桥接模式与策略模式、适配器模式有何异同?

这几个设计模式确实在结构上有些相似之处,都涉及到了“委托”或者“封装”,但它们解决的问题和侧重点是不同的。理解它们的异同,能帮助我们更准确地选择合适的模式。

  1. 与策略模式(Strategy Pattern)的异同

    • 相同点:两者都使用了对象组合(Composition)而非继承,并且都通过委托来执行行为。它们都将算法或行为封装在独立的类中。
    • 不同点
      • 目的:策略模式的目的是封装一组可互换的算法,让客户端可以在运行时选择不同的算法。它关注的是“行为”的变化。桥接模式的目的是将抽象和实现分离,让它们可以独立地变化和扩展。它关注的是“结构”和“维度”的分离。
      • 维度:策略模式通常处理一个维度上的变化(不同的算法)。桥接模式处理的是两个或多个正交维度上的变化(抽象的种类和实现的种类)。
      • 层次:策略模式通常在同一抽象层次上提供不同的行为实现。桥接模式则是在不同层次(抽象层和实现层)之间建立桥梁。
    • 举例
      • 策略:计算税费有多种方法(普通税、增值税、消费税),你可以把这些方法封装成不同的策略,然后根据需要切换。
      • 桥接:绘制圆形,可以在OpenGL上画,也可以在DirectX上画。圆形是抽象,OpenGL/DirectX是实现。
  2. 与适配器模式(Adapter Pattern)的异同

    • 相同点:两者都涉及到一个类“使用”另一个类,并且都可能涉及到接口的转换。
    • 不同点
      • 目的:适配器模式的目的是让一个不兼容的接口变得兼容,通常是为了让两个本来无法协同工作的接口能够一起工作。它解决的是“接口不匹配”的问题。桥接模式的目的是分离抽象和实现,允许它们独立演化。它解决的是“多维度变化导致类爆炸”的问题。
      • 方向:适配器模式通常是单向的,将一个现有接口转换为目标接口。桥接模式则是双向的,它在抽象和实现之间建立一个稳定的连接点,两者都可以独立扩展。
      • 设计时机:适配器模式通常是在系统已经存在,需要集成现有不兼容组件时使用(事后弥补)。桥接模式更多是在系统设计之初,预见到多维度变化时使用(事前规划)。
    • 举例
      • 适配器:你有一个老旧的LegacyLogger接口,但你的新系统只认识NewLogger接口,你需要一个LegacyLoggerAdapter来把LegacyLogger包装成NewLogger
      • 桥接:同上文的图形绘制或跨平台UI。

总的来说,桥接模式是当你预见到系统将会有两个或更多个独立变化的维度时,用来解耦和避免类爆炸的利器。它让你的设计更加灵活,更易于维护和扩展。当然,引入桥接模式会增加一些类的数量和间接性,所以并非所有简单场景都适用,它更适合那些复杂度较高、变化频繁的系统。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1018

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

62

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

400

2025.12.29

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

388

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

571

2023.08.10

windows查看端口占用情况
windows查看端口占用情况

Windows端口可以认为是计算机与外界通讯交流的出入口。逻辑意义上的端口一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。怎么查看windows端口占用情况呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

576

2023.07.26

查看端口占用情况windows
查看端口占用情况windows

端口占用是指与端口关联的软件占用端口而使得其他应用程序无法使用这些端口,端口占用问题是计算机系统编程领域的一个常见问题,端口占用的根本原因可能是操作系统的一些错误,服务器也可能会出现端口占用问题。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1099

2023.07.27

windows照片无法显示
windows照片无法显示

当我们尝试打开一张图片时,可能会出现一个错误提示,提示说"Windows照片查看器无法显示此图片,因为计算机上的可用内存不足",本专题为大家提供windows照片无法显示相关的文章,帮助大家解决该问题。

790

2023.08.01

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

0

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.1万人学习

Git 教程
Git 教程

共21课时 | 2.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号