C++ 实现桥接模式(Bridge Pattern)

项目介绍

桥接模式(Bridge Pattern)是一种结构型设计模式,它旨在将抽象与实现解耦,使得两者可以独立变化。通常,抽象和实现的变化是紧密耦合的,这使得系统的扩展变得困难。桥接模式通过引入一个桥接接口,将抽象部分和实现部分分离,允许它们各自独立地变化。

什么是桥接模式

桥接模式的核心思想是:

  • 抽象部分(如接口或抽象类)和实现部分(如具体类)分离,使得二者可以独立地变化。
  • 抽象类通过一个桥接接口来引用实现类,避免了在抽象和实现之间产生紧密的耦合关系。

在桥接模式中,抽象类和实现类分别位于不同的层次,通过“桥接”接口连接起来,从而形成两者的解耦。这种设计使得系统能够在不修改抽象层的情况下,增加新的实现层,反之亦然。

相关知识

  1. 接口与实现分离

    • 桥接模式的核心在于将抽象和实现分开,这通常通过接口来实现。抽象类依赖于接口,而具体实现类实现这些接口。
  2. 多态与扩展性

    • 桥接模式增强了系统的灵活性和扩展性。通过将实现类和抽象类分离,系统可以在不修改现有代码的基础上,方便地引入新的实现类或抽象类。
  3. 桥接模式与其他设计模式的关系

    • 桥接模式与工厂模式结合可以更加方便地创建具体的实现类。
    • 桥接模式与策略模式结合时,策略可以通过桥接模式来选择具体的实现,从而实现灵活的功能切换。

项目背景

假设我们正在设计一个图形绘制系统,其中包含不同类型的图形(如圆形、矩形等),并且每种图形都有不同的绘制方式(例如,使用不同的画笔绘制)。通过桥接模式,我们可以将图形的抽象部分(例如,图形类型)与具体的绘制方式(例如,画笔类型)分离,使得它们可以独立扩展。

项目实现思路

  1. 定义抽象部分:创建一个 Shape 类,表示图形的抽象部分。它包含一个指向实现接口的指针,通过该指针来实现具体的绘制方式。
  2. 定义实现部分:创建一个 DrawingAPI 接口,定义绘制图形的方法。不同的实现类(如 DrawingAPI1DrawingAPI2)提供具体的绘制方式。
  3. 实现具体的抽象类:创建具体的图形类(如 Circle),它实现 Shape 接口并持有一个 DrawingAPI 接口的实例,委托绘制任务给具体的 DrawingAPI 实现类。
  4. 客户端使用桥接模式:客户端可以通过不同的 ShapeDrawingAPI 组合来创建具有不同绘制方式的图形。

项目实现代码

#include <iostream>
#include <memory>

// 绘制接口:实现部分
class DrawingAPI {
public:
    virtual void drawCircle(double x, double y, double radius) const = 0;
    virtual void drawRectangle(double x, double y, double width, double height) const = 0;
    virtual ~DrawingAPI() = default;
};

// 具体实现类:DrawingAPI1
class DrawingAPI1 : public DrawingAPI {
public:
    void drawCircle(double x, double y, double radius) const override {
        std::cout << "DrawingAPI1: 绘制圆形, 位置(" << x << ", " << y << "), 半径: " << radius << std::endl;
    }

    void drawRectangle(double x, double y, double width, double height) const override {
        std::cout << "DrawingAPI1: 绘制矩形, 位置(" << x << ", " << y << "), 宽度: " << width << ", 高度: " << height << std::endl;
    }
};

// 具体实现类:DrawingAPI2
class DrawingAPI2 : public DrawingAPI {
public:
    void drawCircle(double x, double y, double radius) const override {
        std::cout << "DrawingAPI2: 绘制圆形, 位置(" << x << ", " << y << "), 半径: " << radius << std::endl;
    }

    void drawRectangle(double x, double y, double width, double height) const override {
        std::cout << "DrawingAPI2: 绘制矩形, 位置(" << x << ", " << y << "), 宽度: " << width << ", 高度: " << height << std::endl;
    }
};

// 抽象部分:图形
class Shape {
protected:
    std::shared_ptr<DrawingAPI> drawingAPI;
public:
    Shape(std::shared_ptr<DrawingAPI> api) : drawingAPI(api) {}
    virtual void draw() const = 0;
    virtual void resize(double factor) = 0;
    virtual ~Shape() = default;
};

// 具体图形类:圆形
class Circle : public Shape {
private:
    double x, y, radius;

public:
    Circle(double x, double y, double radius, std::shared_ptr<DrawingAPI> api)
        : Shape(api), x(x), y(y), radius(radius) {}

    void draw() const override {
        drawingAPI->drawCircle(x, y, radius);
    }

    void resize(double factor) override {
        radius *= factor;
    }
};

// 具体图形类:矩形
class Rectangle : public Shape {
private:
    double x, y, width, height;

public:
    Rectangle(double x, double y, double width, double height, std::shared_ptr<DrawingAPI> api)
        : Shape(api), x(x), y(y), width(width), height(height) {}

    void draw() const override {
        drawingAPI->drawRectangle(x, y, width, height);
    }

    void resize(double factor) override {
        width *= factor;
        height *= factor;
    }
};

// 客户端代码
int main() {
    // 创建具体的绘制实现
    std::shared_ptr<DrawingAPI> api1 = std::make_shared<DrawingAPI1>();
    std::shared_ptr<DrawingAPI> api2 = std::make_shared<DrawingAPI2>();

    // 创建具体图形,并指定不同的绘制实现
    std::shared_ptr<Shape> circle1 = std::make_shared<Circle>(1.0, 2.0, 3.0, api1);
    std::shared_ptr<Shape> rectangle1 = std::make_shared<Rectangle>(2.0, 3.0, 4.0, 5.0, api1);

    std::shared_ptr<Shape> circle2 = std::make_shared<Circle>(1.0, 2.0, 3.0, api2);
    std::shared_ptr<Shape> rectangle2 = std::make_shared<Rectangle>(2.0, 3.0, 4.0, 5.0, api2);

    // 绘制图形
    std::cout << "使用 DrawingAPI1 绘制图形:" << std::endl;
    circle1->draw();
    rectangle1->draw();

    std::cout << "\n使用 DrawingAPI2 绘制图形:" << std::endl;
    circle2->draw();
    rectangle2->draw();

    // 调整图形大小
    std::cout << "\n调整图形大小(倍数:1.5):" << std::endl;
    circle1->resize(1.5);
    rectangle1->resize(1.5);

    circle1->draw();
    rectangle1->draw();

    return 0;
}

代码解读

1. DrawingAPI 接口

DrawingAPI 是桥接模式中的实现部分接口,它定义了绘制图形的基本方法 drawCircle()。具体的绘制方式(如 DrawingAPI1DrawingAPI2)需要实现该接口,提供具体的绘制逻辑。

2. Shape 抽象类

Shape 是桥接模式中的抽象部分接口,表示不同的形状。Shape 类包含一个指向 DrawingAPI 接口的指针,这样不同的形状可以使用不同的绘制方式(例如:线条绘制或填充绘制)。Shape 提供了 draw()resize() 方法,分别用于绘制图形和调整图形大小。

3. Circle

Circle 类是 Shape 的一个具体实现,表示一个圆形。它通过组合一个 DrawingAPI 对象来选择具体的绘制方式,并提供 draw()resize() 方法来绘制圆形和调整圆形的大小。

4. 具体绘制方式

DrawingAPI1DrawingAPI2 是两种具体的绘制方式,分别表示线条绘制和填充绘制。它们都实现了 DrawingAPI 接口,并提供了具体的绘制方法。

5. 客户端代码

在客户端代码中,我们首先创建了不同的 DrawingAPI 实现(api1api2)。然后创建了 Circle 对象,并选择不同的绘制方式。每个 Circle 对象都可以通过不同的绘制方式来绘制自身,且可以独立地调整大小。最后,我们通过调用 draw()resize() 方法来完成绘制和大小调整操作。

项目总结

桥接模式通过将抽象部分和实现部分分离,使得它们能够独立变化,从而减少了类的复杂性和继承关系。它允许我们通过组合的方式灵活地改变抽象部分和实现部分之间的关系,避免了继承树的膨胀。

桥接模式的优点:

  1. 分离抽象和实现:通过将抽象部分与实现部分分离,我们可以在不改变抽象类的情况下,修改具体实现。
  2. 提高灵活性:可以独立地扩展抽象类和实现类,而不需要修改现有代码。
  3. 避免继承的复杂性:通过组合代替继承,避免了继承树过于复杂的问题。

桥接模式的缺点:

  1. 增加类的数量:桥接模式引入了更多的类,可能会导致系统中的类数增多。
  2. 设计复杂:初期的设计可能需要更多的思考,以便正确地分离抽象部分和实现部分。

适用场景:

  • 系统需要在抽象化和实现之间做出独立扩展的场景。
  • 有多个不同的变体(如不同的绘制方式、不同的文件存储方式等),并且这些变体可能会与其他系统产生冲突时。

总结:

桥接模式提供了一种优雅的方式来解耦抽象部分和实现部分,使得每一部分可以独立变化。通过桥接模式,我们可以避免传统继承的复杂性,使得系统更具灵活性和可扩展性。在本项目中,我们通过将形状与绘制方式分离,实现了圆形的不同绘制方式,并展示了如何灵活地扩展形状和绘制方式。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐