• C++设计模式之——桥接模式详解和代码实例



    桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与其实现部分分离,使它们都可以独立变化。这种模式有时也被称作柄体(Handle and Body)模式或接口隔离模式。它的主要目的是将抽象层与实现层解耦,使得两者可以独立扩展而互不影响。

    桥接模式详解:

    1. 角色定义

      • Abstraction(抽象化角色):定义抽象接口,维护一个对Implementor对象的引用。
      • RefinedAbstraction(具体抽象化角色):是Abstraction的一个子类,对Abstraction接口进行一些补充。
      • Implementor(实现化角色):定义实现化角色的接口,任何Abstraction角色可以调用。
      • ConcreteImplementor(具体实现化角色):Implementor接口的具体实现。
    2. 工作原理

      • 抽象化角色持有实现化角色的引用,它只需要定义与客户端的接口并委托给实现化角色来执行。
      • 具体化角色继承抽象化角色并实现自己的业务逻辑,同时构造时关联相应的实现化角色。

    C++代码实例

    #include 
    
    // 实现化角色接口
    class Implementor {
    public:
        virtual ~Implementor() {}
        virtual void operationImpl() = 0; // 定义操作接口
    };
    
    // 具体实现化角色A
    class ConcreteImplementorA : public Implementor {
    public:
        void operationImpl() override {
            std::cout << "ConcreteImplementorA 的 operationImpl()" << std::endl;
        }
    };
    
    // 具体实现化角色B
    class ConcreteImplementorB : public Implementor {
    public:
        void operationImpl() override {
            std::cout << "ConcreteImplementorB 的 operationImpl()" << std::endl;
        }
    };
    
    // 抽象化角色
    class Abstraction {
    protected:
        Implementor* _implementor;
    
    public:
        Abstraction(Implementor* impl) : _implementor(impl) {}
        virtual ~Abstraction() { delete _implementor; }
        virtual void operation() = 0; // 定义抽象操作
    };
    
    // 具体抽象化角色
    class RefinedAbstraction : public Abstraction {
    public:
        RefinedAbstraction(Implementor* impl) : Abstraction(impl) {}
    
        void operation() override {
            std::cout << "RefinedAbstraction 调用 operation..." << std::endl;
            _implementor->operationImpl(); // 委托实现化角色执行操作
        }
    };
    
    int main() {
        Abstraction* absA = new RefinedAbstraction(new ConcreteImplementorA());
        absA->operation();
    
        Abstraction* absB = new RefinedAbstraction(new ConcreteImplementorB());
        absB->operation();
    
        delete absA;
        delete absB;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    在这个例子中,Abstraction是抽象化角色,RefinedAbstraction是具体抽象化角色,Implementor是实现化角色接口,ConcreteImplementorAConcreteImplementorB是具体实现化角色。通过改变构造Abstraction时传入的Implementor的不同实现,我们可以轻松切换不同的行为方式。

    桥接模式的核心价值在于它能分离抽象和实现,让两者可以独立演化。下面是对上述代码实例的进一步解释:

    1. 抽象化角色(Abstraction):定义了抽象的操作operation(),但不提供具体实现。它通过维持一个指向Implementor的指针来间接使用其实现。
    class Abstraction {
    protected:
        Implementor* _implementor;
    
    public:
        // 构造函数接收一个Implementor对象的指针
        Abstraction(Implementor* impl) : _implementor(impl) {}
        virtual ~Abstraction() { delete _implementor; }
    
        // 定义抽象操作
        virtual void operation() = 0;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 具体抽象化角色(RefinedAbstraction):继承自Abstraction,在operation()中调用了_implementoroperationImpl()方法,这样就把具体实现细节交给了实现化角色。
    class RefinedAbstraction : public Abstraction {
    public:
        RefinedAbstraction(Implementor* impl) : Abstraction(impl) {}
    
        // 实现抽象操作,调用Implementor的实现
        void operation() override {
            std::cout << "RefinedAbstraction 调用 operation..." << std::endl;
            _implementor->operationImpl();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 实现化角色(Implementor):定义了所有实现类共有的接口operationImpl(),它是抽象化角色依赖的具体操作。
    class Implementor {
    public:
        virtual ~Implementor() {}
        // 实现化角色的接口
        virtual void operationImpl() = 0;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 具体实现化角色(ConcreteImplementorA/B):分别提供了operationImpl()的不同实现。
    class ConcreteImplementorA : public Implementor {
    public:
        void operationImpl() override {
            std::cout << "ConcreteImplementorA 的 operationImpl()" << std::endl;
        }
    };
    
    class ConcreteImplementorB : public Implementor {
    public:
        void operationImpl() override {
            std::cout << "ConcreteImplementorB 的 operationImpl()" << std::endl;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在main函数中,我们可以根据需要为Abstraction实例化不同的Implementor对象,从而达到在运行时动态改变系统行为的目的,这就是桥接模式的关键所在。通过这种方式,抽象与实现得以解耦,各自可以独立扩展而不影响对方。

    进一步阐述桥接模式的优点和适用场景

    优点:

    1. 松耦合:桥接模式使得抽象和实现能够独立地变化和发展,提高系统的可扩展性。
    2. 灵活性:由于抽象部分和实现部分被彻底分开,因此可以根据需要组合不同的抽象和实现,增强了系统的灵活性。
    3. 避免多重继承:在某些情况下,如果采用传统的继承机制,可能会导致类层次过深或出现菱形继承问题,桥接模式则可以有效地避免这些问题,通过组合而非继承来实现复用。

    适用场景:

    1. 系统需要在多个维度上进行变化:例如,操作系统既有GUI版又有命令行版,GUI版又支持多种窗口系统,这时就可以使用桥接模式把操作系统核心与用户界面和窗口系统分离开来。
    2. 希望在运行时切换实现:例如,不同的数据库引擎有不同的实现,但是上层应用的查询语句是通用的,此时可以用桥接模式,在运行时根据需求切换数据库引擎。
    3. 一个类存在两个独立变化的维度,且这两个维度需要进行组合:例如打印机既可以打印黑白也可以打印彩色,打印目标既可以是纸张也可以是PDF,这时就可以使用桥接模式将颜色模式和输出格式解耦。

    总结来说,桥接模式在设计中鼓励关注点分离,让抽象和实现各自独立演进,提高了代码的可维护性和复用性。在实际编程过程中,尤其是在设计复杂的面向对象系统时,合理运用桥接模式可以显著提升软件的设计质量。

    桥接模式的实际应用场景还包括但不限于以下几种情况:

    1. 图形界面库:图形界面库通常有多种外观(如按钮、菜单等)和多种平台实现(Windows、Mac、Linux等)。桥接模式可以使抽象外观接口独立于各个平台的具体实现,方便在不同平台上复用相同的外观设计。

    2. 日志系统:日志系统可能需要支持不同的日志级别(如Debug、Info、Error等)和不同的输出目的地(如控制台、文件、数据库等)。桥接模式可以将日志级别抽象出来,同时将输出目的地作为一个独立的维度,使得日志系统可以在不修改原有代码的基础上轻易更换日志级别或输出目的地。

    3. 数据库驱动:数据库驱动程序可能需要支持不同的数据库(MySQL、Oracle、SQL Server等)和不同的操作(查询、插入、删除、更新等)。桥接模式可以将数据库操作抽象成统一的接口,而具体的数据库操作由不同的实现类负责,这样就能灵活地替换或升级数据库驱动。

    4. 图像处理库:图像处理库可能需要支持多种图像格式(JPEG、PNG、GIF等)和多种处理操作(缩放、旋转、裁剪等)。桥接模式可以将图像处理操作抽象化,并与各种图像格式的读写实现解耦,便于添加新的格式支持或处理功能。

    总之,桥接模式在面对抽象和实现之间需要独立扩展的问题时,能够帮助我们构建更加灵活、易于维护和扩展的设计方案。在实际开发中,识别出可能需要桥接模式的场景并正确运用,能够显著提升系统的可维护性和可扩展性。

    另外,桥接模式也适用于以下情况:

    1. 网络通信框架:在网络通信框架中,抽象接口可以定义诸如发送消息、接收消息等功能,而具体实现可以包括TCP、UDP、WebSocket等多种协议。这样,上层应用只需关注消息的发送和接收逻辑,而无需关心底层通信协议的具体实现。

    2. 多语言本地化:在国际化应用中,可以将界面的逻辑(如按钮、标签、提示信息等)与具体语言环境(如英文、中文、法文等)分离,通过桥接模式使得在切换不同语言环境时,仅需替换具体的本地化实现即可。

    3. 硬件驱动程序:对于硬件设备(如打印机、扫描仪等),抽象接口可以定义一系列设备操作,而具体实现可以针对不同的厂商和型号。这样,即使更换了硬件设备,只要新设备遵循同一接口规范,应用也能无缝对接。

    4. 支付系统:支付系统可能需要对接多种支付渠道(如支付宝、微信支付、银行卡支付等),并且每种支付渠道的操作流程各不相同。桥接模式可以将支付操作抽象化,而将具体支付渠道的处理逻辑放在各自的实现类中。

    桥接模式不仅限于以上场景,它强调的是将抽象部分与实现部分相分离,使其可以独立变化。在软件工程实践中,遇到类似的多维度变化需求时,都应该考虑是否可以采用桥接模式进行设计。

    python推荐学习汇总连接:
    50个开发必备的Python经典脚本(1-10)

    50个开发必备的Python经典脚本(11-20)

    50个开发必备的Python经典脚本(21-30)

    50个开发必备的Python经典脚本(31-40)

    50个开发必备的Python经典脚本(41-50)
    ————————————————

    ​最后我们放松一下眼睛
    在这里插入图片描述

  • 相关阅读:
    笔试题【day30】
    智能搬运小车(自动抓取、循迹)
    brew install libomp失败解决方法
    SQL基础知识笔记
    上采样(upsample/upsacle)下采样(downsample/downscale)的作用
    SpringBoot和Vue集成Markdown和多级评论——基于SpringBoot和Vue的后台管理系统项目系列博客(二十三)
    Golang实现超时机制读取文件
    判断两个链表是否相交
    大模型的魔法
    每天一个新知识之 SpringBoot+Dubbo 的快速入门
  • 原文地址:https://blog.csdn.net/qqrrjj2011/article/details/136365605