C++ 设计模式——桥接模式

C++ 设计模式——桥接模式

桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们都可以独立地变化。这种模式通过组合的方式来代替继承,增加了系统的灵活性。

1. 主要组成成分

  1. 抽象类(Abstraction):定义抽象类的接口,维护一个对实现类对象的引用。
  2. 扩展抽象类(Refined Abstraction):扩展抽象类的接口。
  3. 实现类接口(Implementor):定义实现类的接口。
  4. 具体实现类(Concrete Implementor):实现实现类接口并定义具体实现。

2. 逐步构建桥接模式

以图像处理系统为例,展示桥接模式的实现,在图像处理系统中,可以识别出两个独立变化的维度:

  • 图像格式(PNG, JPG, BMP等)
  • 操作系统(Windows, Linux, Mac等)

这两个维度可以独立变化,非常适合使用桥接模式。

步骤1: 创建实现接口

创建操作系统的实现接口,此接口定义了所有操作系统都应实现的draw方法。

// 操作系统接口
class ImageOS {
public:
    // 纯虚函数,用于在特定操作系统上绘制图像
    virtual void draw(char* pData, int iLen) = 0;
    virtual ~ImageOS() {}
};
步骤2: 实现具体实现类

为不同的操作系统创建具体的实现类。

// Windows操作系统实现
class ImageOS_Windows : public ImageOS {
public:
    virtual void draw(char* pData, int iLen) override {
        std::cout << "在Windows操作系统下显示图像数据。" << std::endl;
    }
};

// Linux操作系统实现
class ImageOS_Linux : public ImageOS {
public:
    virtual void draw(char* pData, int iLen) override {
        std::cout << "在Linux操作系统下显示图像数据。" << std::endl;
    }
};

// Mac操作系统实现
class ImageOS_Mac : public ImageOS {
public:
    virtual void draw(char* pData, int iLen) override {
        std::cout << "在Mac操作系统下显示图像数据。" << std::endl;
    }
};
步骤3: 创建抽象类

创建图像格式的抽象类,这个抽象类包含一个ImageOS的指针,构成了"桥"的关键部分。

// 图像格式抽象类
class ImageFormat {
protected:
    ImageOS* m_pImgOS;  // 操作系统接口指针
public:
    ImageFormat(ImageOS* pimgos) : m_pImgOS(pimgos) {}
    // 纯虚函数,用于解析特定格式的图像文件
    virtual void parseFile(const char* pfilename) = 0;
    virtual ~ImageFormat() {}
};
步骤4: 实现扩展抽象类

为不同的图像格式创建扩展抽象类。

// PNG图像格式实现
class Image_PNG : public ImageFormat {
public:
    Image_PNG(ImageOS* pimgos) : ImageFormat(pimgos) {}
    virtual void parseFile(const char* pfilename) override {
        std::cout << "解析PNG文件数据,";
        int iLen = 100;
        std::unique_ptr<char[]> presult(new char[iLen]);
        m_pImgOS->draw(presult.get(), iLen);
    }
};

// JPG图像格式实现
class Image_JPG : public ImageFormat {
public:
    Image_JPG(ImageOS* pimgos) : ImageFormat(pimgos) {}
    virtual void parseFile(const char* pfilename) override {
        std::cout << "解析JPG文件数据,";
        // 实现细节省略
    }
};

// BMP图像格式实现
class Image_BMP : public ImageFormat {
public:
    Image_BMP(ImageOS* pimgos) : ImageFormat(pimgos) {}
    virtual void parseFile(const char* pfilename) override {
        std::cout << "解析BMP文件数据,";
        // 实现细节省略
    }
};
步骤5: 客户端使用

在客户端代码中使用这个桥接模式。

int main() {
    // 创建Windows操作系统对象
    std::unique_ptr<ImageOS> pimgos_windows = std::make_unique<ImageOS_Windows>();
    
    // 创建PNG图像格式对象,并关联到Windows操作系统
    std::unique_ptr<ImageFormat> pimg_png = std::make_unique<Image_PNG>(pimgos_windows.get());
    
    // 解析PNG文件
    pimg_png->parseFile("image.png");

    return 0;
}

3. 桥接模式 UML 图

桥接模式 UML 图

UML 图解析

桥接模式的 UML 图中包含 4 种角色:

  • Abstraction (抽象部分相关接口):定义抽象类的接口,其中包含一根指向 Implementor 类型对象的指针。这里指 ImageFormat 类。
  • RefinedAbstraction (扩充抽象部分接口):实现在 Abstraction 中定义的接口,还可以在其中调用在 Implementor 中定义的方法。这里指 Image_PNGImage_JPGImage_BMP 类。
  • Implementor (实现部分相关接口):定义实现类的接口,这些接口可能与 Abstraction 中的接口一致,也可能完全不同。通常 Implementor 中定义的接口仅提供基本操作,而 Abstraction 中定义的接口会实现更多、更复杂的功能。这里指 ImageOS 类。
  • ConcreteImplementor (实现部分具体类):实现在 Implementor 中定义的接口。这里指 ImageOS_WindowsImageOS_LinuxImageOS_Mac 类。

4. 桥接模式的优点

  1. 解耦抽象与实现:桥接模式通过将抽象部分与实现部分分离,实现了两者的解耦,使它们可以独立变化。
  2. 提高可扩展性:该模式允许在不修改现有代码的情况下,独立地扩展抽象和实现层次结构。
  3. 实现细节封装:客户端代码仅与高层抽象进行交互,无需关注底层实现细节。
  4. 改善类层次结构:通过组合代替多层继承,桥接模式减少了类的数量和复杂性。
  5. 进松耦合:抽象和实现的分离降低了系统各部分之间的依赖,有利于系统的维护和演化。

5. 桥接模式的缺点

  1. 增加设计复杂度:桥接模式要求系统具有抽象层次的设计和编程能力,可能增加初始开发的复杂度。
  2. 识别抽象和实现的难度:正确识别系统中的抽象和实现维度需要丰富的经验和深入的系统理解。
  3. 潜在的性能影响:由于引入了额外的间接层和方法调用,在某些情况下可能会轻微影响系统性能。
  4. 增加代码量:相比简单的继承方案,桥接模式通常需要更多的类和接口定义。

6. 桥接模式适用场景

  1. 多维度变化:当一个系统需要在多个维度上独立变化,且这些维度之间存在正交关系时。
  2. 跨平台开发:在需要开发跨多个平台的应用程序时,桥接模式可以有效地分离平台相关代码。
  3. 运行时切换实现:当系统需要在运行时动态切换不同实现时,桥接模式提供了灵活的结构支持。
  4. 避免类爆炸:在可能导致类层次结构急剧膨胀的情况下,桥接模式提供了一种替代多层继承的方案。
  5. 接口与实现解耦:当需要将接口与实现分离,使它们可以独立演化时。
  6. 可插拔组件:在设计可插拔的组件系统时,桥接模式可以提供清晰的结构支持。
  7. 不同维度的组合:当系统中存在多个独立的维度,需要灵活组合这些维度时。

总结

桥接模式是一种强大的设计模式,它通过将抽象部分与实现部分分离,提供了更高的灵活性和可扩展性。虽然它增加了设计的复杂度,但其带来的解耦和扩展性优势使其在许多复杂系统中成为不可或缺的设计工具。通过桥接模式,可以有效地应对多维度变化的需求,避免类爆炸,提高系统的维护性和演化能力。

完整代码

#include <iostream>
#include <memory>
#include <string>

// 操作系统接口
class ImageOS {
public:
    virtual void draw(const char* pData, int iLen) = 0;
    virtual ~ImageOS() = default;
};

// Windows操作系统实现
class ImageOS_Windows : public ImageOS {
public:
    void draw(const char* pData, int iLen) override {
        std::cout << "在Windows操作系统下显示图像数据。" << std::endl;
    }
};

// Linux操作系统实现
class ImageOS_Linux : public ImageOS {
public:
    void draw(const char* pData, int iLen) override {
        std::cout << "在Linux操作系统下显示图像数据。" << std::endl;
    }
};

// Mac操作系统实现
class ImageOS_Mac : public ImageOS {
public:
    void draw(const char* pData, int iLen) override {
        std::cout << "在Mac操作系统下显示图像数据。" << std::endl;
    }
};

// 图像格式抽象类
class ImageFormat {
protected:
    std::shared_ptr<ImageOS> m_pImgOS;
public:
    explicit ImageFormat(std::shared_ptr<ImageOS> pimgos) : m_pImgOS(std::move(pimgos)) {}
    virtual void parseFile(const std::string& filename) = 0;
    virtual ~ImageFormat() = default;
};

// PNG图像格式实现
class Image_PNG : public ImageFormat {
public:
    explicit Image_PNG(std::shared_ptr<ImageOS> pimgos) : ImageFormat(std::move(pimgos)) {}
    void parseFile(const std::string& filename) override {
        std::cout << "解析PNG文件数据:" << filename << ",";
        const int iLen = 100;
        std::unique_ptr<char[]> presult = std::make_unique<char[]>(iLen);
        m_pImgOS->draw(presult.get(), iLen);
    }
};

// JPG图像格式实现
class Image_JPG : public ImageFormat {
public:
    explicit Image_JPG(std::shared_ptr<ImageOS> pimgos) : ImageFormat(std::move(pimgos)) {}
    void parseFile(const std::string& filename) override {
        std::cout << "解析JPG文件数据:" << filename << ",";
        // 实现细节省略
        m_pImgOS->draw(nullptr, 0);
    }
};

// BMP图像格式实现
class Image_BMP : public ImageFormat {
public:
    explicit Image_BMP(std::shared_ptr<ImageOS> pimgos) : ImageFormat(std::move(pimgos)) {}
    void parseFile(const std::string& filename) override {
        std::cout << "解析BMP文件数据:" << filename << ",";
        // 实现细节省略
        m_pImgOS->draw(nullptr, 0);
    }
};

int main() {
    // 创建不同操作系统对象
    auto windows_os = std::make_shared<ImageOS_Windows>();
    auto linux_os = std::make_shared<ImageOS_Linux>();
    auto mac_os = std::make_shared<ImageOS_Mac>();

    // 创建不同图像格式对象,并关联到不同操作系统
    auto png_windows = std::make_unique<Image_PNG>(windows_os);
    auto jpg_linux = std::make_unique<Image_JPG>(linux_os);
    auto bmp_mac = std::make_unique<Image_BMP>(mac_os);

    // 解析不同格式的图像文件
    png_windows->parseFile("image.png");
    jpg_linux->parseFile("photo.jpg");
    bmp_mac->parseFile("picture.bmp");

    return 0;
}
  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值