C++ 桥接模式 (Bridge Pattern)
flyfish
桥接模式是一种结构型设计模式,旨在将抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式可以使一个类的功能层次结构与实现层次结构分离。它通过引入一个中间接口(桥接接口)将具体的实现与抽象分离,适用于需要跨多个不同维度来扩展的系统。最简单的说法就是通过一个桥梁来连接两个独立变化的部分,使它们可以独立地变化和扩展。
看代码例子可以更清晰的理解桥接模式 (Bridge Pattern)
在以下情况下使用桥接模式:
当不希望在抽象和实现之间有紧密的耦合:使用桥接模式可以将抽象和实现分离,使得它们可以独立变化。
当希望在运行时更改实现:可以动态地选择不同的实现,例如不同的绘图API或不同的通信协议。当希望一个类在多个维度上变化时,使用桥接模式。当一个类有多个可能的实现方式,并且这些实现方式可以动态切换时,使用桥接模式。
当希望通过组合而不是继承来扩展类的功能:桥接模式比传统的继承更灵活,可以避免复杂的类层次结构。
桥接模式的 C++ 代码示例
绘制圆形,并结合具体的绘图API来说明桥接模式的用法。
功能层次结构(抽象部分):
Shape:抽象类,定义了形状的接口,包含一个指向DrawingAPI的引用。
CircleShape:具体的形状类,继承自Shape,实现了具体的圆形绘制和调整大小的功能。
实现层次结构(具体实现部分):
DrawingAPI:抽象接口类,定义了绘制圆形的接口。
DrawingAPI01和DrawingAPI02:具体实现类,分别实现了不同的绘制圆形的方法。
c
#include <iostream>
#include <string>
#include <vector>
// 抽象接口类:绘图API,定义绘制圆的接口
class DrawingAPI {
public:
virtual ~DrawingAPI() = default;
// 纯虚函数,绘制圆形,具体实现由子类提供
virtual std::string DrawCircle(float x, float y, float radius) const = 0;
};
// 具体实现类01:使用API01绘制圆形
class DrawingAPI01 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API01.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
};
// 具体实现类02:使用API02绘制圆形
class DrawingAPI02 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API02.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
};
// 抽象类:形状,包含一个指向绘图API的引用
class Shape {
public:
// 构造函数,初始化绘图API引用
Shape(const DrawingAPI& drawing_api) : drawing_api_(drawing_api) {}
virtual ~Shape() = default;
// 纯虚函数,绘制形状,由子类实现
virtual std::string Draw() const = 0;
// 纯虚函数,按百分比调整大小,由子类实现
virtual float ResizeByPercentage(const float percent) = 0;
protected:
const DrawingAPI& drawing_api_; // 绘图API引用
};
// 具体实现类:圆形
class CircleShape : public Shape {
public:
// 构造函数,初始化圆形位置、半径和绘图API
CircleShape(float x, float y, float radius, const DrawingAPI& drawing_api)
: Shape(drawing_api), x_(x), y_(y), radius_(radius) {}
// 绘制圆形,调用绘图API的DrawCircle方法
std::string Draw() const override {
return drawing_api_.DrawCircle(x_, y_, radius_);
}
// 按百分比调整圆形的半径
float ResizeByPercentage(const float percent) override {
return radius_ *= (1.0f + percent / 100.0f);
}
private:
float x_, y_, radius_; // 圆形的位置和半径
};
int main(int argc, char** argv) {
const DrawingAPI01 api1{}; // 创建绘图API01实例
const DrawingAPI02 api2{}; // 创建绘图API02实例
std::vector<CircleShape> shapes {
// 创建圆形实例,使用不同的绘图API
CircleShape{1.0f, 2.0f, 3.0f, api1},
CircleShape{5.0f, 7.0f, 11.0f, api2}
};
// 调整圆形大小并绘制
for (auto& shape: shapes) {
shape.ResizeByPercentage(2.5); // 调整半径
std::cout << shape.Draw() << std::endl; // 绘制圆形
}
return 0;
}
桥接模式中的类在多个维度上的变化,扩展形状的种类和绘图API的实现
-
形状的种类(Shape) :可以有不同的形状,如圆形(CircleShape)、矩形(RectangleShape)等。这个维度代表了形状的多样性。
-
绘图API的实现(DrawingAPI) :可以有不同的绘图实现,如DrawingAPI01、DrawingAPI02等。这个维度代表了绘图实现的多样性。
通过将这两个维度的变化分离,可以独立地扩展形状的种类和绘图API的实现,而不需要修改现有代码。这体现了桥接模式的灵活性。
代码中的多个维度变化的具体实现
1. 形状的变化
形状的变化体现在不同的Shape子类中:
cpp
class Shape {
public:
Shape(const DrawingAPI& drawing_api) : drawing_api_(drawing_api) {}
virtual ~Shape() = default;
virtual std::string Draw() const = 0;
virtual float ResizeByPercentage(const float percent) = 0;
protected:
const DrawingAPI& drawing_api_;
};
class CircleShape : public Shape {
public:
CircleShape(float x, float y, float radius, const DrawingAPI& drawing_api)
: Shape(drawing_api), x_(x), y_(y), radius_(radius) {}
std::string Draw() const override {
return drawing_api_.DrawCircle(x_, y_, radius_);
}
float ResizeByPercentage(const float percent) override {
return radius_ *= (1.0f + percent / 100.0f);
}
private:
float x_, y_, radius_;
};
可以进一步扩展形状类,例如添加一个矩形类:
cpp
class RectangleShape : public Shape {
public:
RectangleShape(float x, float y, float width, float height, const DrawingAPI& drawing_api)
: Shape(drawing_api), x_(x), y_(y), width_(width), height_(height) {}
std::string Draw() const override {
return drawing_api_.DrawRectangle(x_, y_, width_, height_);
}
float ResizeByPercentage(const float percent) override {
width_ *= (1.0f + percent / 100.0f);
height_ *= (1.0f + percent / 100.0f);
return width_ * height_;
}
private:
float x_, y_, width_, height_;
};
2. 绘图API增加绘制矩阵的函数
绘图API的变化体现在不同的DrawingAPI实现类中:
cpp
class DrawingAPI {
public:
virtual ~DrawingAPI() = default;
virtual std::string DrawCircle(float x, float y, float radius) const = 0;
virtual std::string DrawRectangle(float x, float y, float width, float height) const = 0;
};
class DrawingAPI01 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API01.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
std::string DrawRectangle(float x, float y, float width, float height) const override {
return "API01.rectangle at " + std::to_string(x) + ":" + std::to_string(y) +
" - width: " + std::to_string(width) + ", height: " + std::to_string(height);
}
};
class DrawingAPI02 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API02.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
std::string DrawRectangle(float x, float y, float width, float height) const override {
return "API02.rectangle at " + std::to_string(x) + ":" + std::to_string(y) +
" - width: " + std::to_string(width) + ", height: " + std::to_string(height);
}
};
运行时选择不同的实现
在主程序中,我们可以在运行时选择不同的绘图API实现,并且可以独立地使用不同的形状对象:
cpp
int main(int argc, char** argv) {
const DrawingAPI01 api1{};
const DrawingAPI02 api2{};
std::vector<Shape*> shapes {
new CircleShape{1.0f, 2.0f, 3.0f, api1},
new RectangleShape{5.0f, 7.0f, 11.0f, 13.0f, api2}
};
for (auto shape : shapes) {
shape->ResizeByPercentage(2.5);
std::cout << shape->Draw() << std::endl;
delete shape;
}
return 0;
}
在这个例子中,CircleShape
和 RectangleShape
代表形状的多样性(一个维度),而 DrawingAPI01
和 DrawingAPI02
代表绘图API实现的多样性(另一个维度)。这样通过一个桥梁来连接两个独立变化的部分,使它们可以独立地变化和扩展。