狂风呼啸,一场强台风正以迅猛之势逼近你所在的城市,带来极大的威胁。而祸不单行,市中心的一座大楼突发火灾,情况万分危急。应急指挥中心里气氛凝重,领导紧盯着屏幕,一边是 GIS 系统中由气象部门实时更新的降雨量、风速数据以及精准的地图信息,这些数据对于掌握台风的动态和影响范围至关重要;另一边则是 CAD 系统中结构工程师精心标注的建筑承重参数,这是评估大楼及周边建筑安全状况的关键依据。
领导需要在极短时间内,综合这些信息快速划定群众疏散范围,同时精确预估周边建筑的受损风险。然而,现实却异常棘手,由于 CAD 和 GIS 系统的数据存在巨大差异,导致二者难以协同工作。CAD 采用的工程坐标系与 GIS 的地理坐标系完全不匹配,根本无法直接进行叠加分析;建筑属性字段与空间分析接口也互不相容,就像两个交流不畅的人,难以达成有效的合作。
这种数据上的割裂让领导不得不频繁在多个平台之间手动转换数据进行计算,每一次操作都繁琐又耗时。每一秒的流逝都可能关乎群众的安危,宝贵的应急响应时间就在这一次次的数据转换中悄然溜走。时间愈发紧迫,领导一边沉稳地发布救援指挥命令,一边忍不住焦急怒吼:“什么时候我们的应急灾害管理系统能高效整合这些数据!”
就在这千钧一发之际,英俊无敌的你挺身而出,提出建议:“我们开发一个适配器来整合这些数据吧!” 适配器就如同为不同型号电源插头量身定制的万能转换器,通过巧妙搭建中间层,能够成功消除系统间的差异。一旦实现,CAD 中的建筑轮廓便能精准、动态地投射到 GIS 的三维地形模型上,钢筋混凝土的力学参数也能与洪水淹没算法等各类灾害分析模型无缝对接,在未来还能为接入卫星遥感、物联网传感等新数据源预留架构弹性,灾害推演的准确性将实现质的飞跃,为救援决策提供更有力的支持。
故事纯属虚构,对于一些需要实时性的功能模块使用适配器模式可能并不合适,实际开发请结合实际选择设计模式。
所以,让我们一起来了解一下适配器模式吧!
适配器模式(Adapter Pattern)是一种结构型设计模式1,它主要用于将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类能够协同工作。
适配器模式就像是一个转换器,它能够把一个类的接口适配成另一个接口,让不兼容的接口之间能够进行通信和协作,以满足特定的业务需求。
下面,我们以来自不同图形库的图形类编写适配器为例子编写示例代码。
来自不同库的图形类
// 来自 图形库1 的圆形类
namespace Lib1Graphics {
class Circle {
private:
double radius;
public:
explicit Circle(double r) : radius(r) {}
// 图形库1绘制圆形方法
void Lib1DrawCircle() const {
std::cout << "Drawing a circle with radius " << radius << " from Lib1Graphics library." << std::endl;
}
};
}
// 来自 图形库2 的矩形类
namespace Lib2Graphics {
class Rectangle {
private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
// 图形库2绘制矩形方法
void Lib2Rectangle() const {
std::cout << "Drawing a rectangle with width " << width << " and height " << height << " from Lib2Graphics library." << std::endl;
}
};
}
适配器
#include
#include
#include
// 目标接口,定义统一的绘制图形方法
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
// 圆形适配器类
class CircleAdapter : public Shape {
private:
const Lib1Graphics::Circle& circle;
public:
explicit CircleAdapter(const Lib1Graphics::Circle& c) : circle(c) {}
// 实现目标接口的 draw 方法,调用老的绘制圆形方法
void draw() override {
circle.Lib1DrawCircle();
}
};
// 矩形适配器类
class RectangleAdapter : public Shape {
private:
const Lib2Graphics::Rectangle& rectangle;
public:
explicit RectangleAdapter(const Lib2Graphics::Rectangle& r) : rectangle(r) {}
// 实现目标接口的 draw 方法,调用新的绘制矩形方法
void draw() override {
rectangle.Lib2Rectangle();
}
};
int main() {
// 创建不同来源的图形对象
Lib1Graphics::Circle oldCircle(5.0);
Lib2Graphics::Rectangle newRectangle(3.0, 4.0);
// 创建适配器对象
CircleAdapter circleAdapter(oldCircle);
RectangleAdapter rectangleAdapter(newRectangle);
// 使用 std::vector 管理 Shape 对象
std::vector> shapes;
shapes.push_back(std::make_unique(oldCircle));
shapes.push_back(std::make_unique(newRectangle));
// 以统一的接口调用绘制方法
for (const auto& shape : shapes) {
shape->draw();
}
return 0;
}
// 来自 Lib1Graphics 的圆形类
class Lib1Circle {
private double radius;
public Lib1Circle(double radius) {
this.radius = radius;
}
// 图形库1绘制圆形方法
public void lib1DrawCircle() {
System.out.println("Drawing a circle with radius " + radius + " from Lib1Graphics library.");
}
}
// 来自 Lib2Graphics 的矩形类
class Lib2Rectangle {
private double width;
private double height;
public Lib2Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
// 图形库2绘制矩形方法
public void lib2DrawRectangle() {
System.out.println("Drawing a rectangle with width " + width + " and height " + height + " from Lib2Graphics library.");
}
}
// 目标接口,定义统一的绘制图形方法
interface Shape {
void draw();
}
// 圆形适配器类
class CircleAdapter implements Shape {
private Lib1Circle circle;
public CircleAdapter(Lib1Circle circle) {
this.circle = circle;
}
// 实现目标接口的 draw 方法,调用老的绘制圆形方法
@Override
public void draw() {
circle.lib1DrawCircle();
}
}
// 矩形适配器类
class RectangleAdapter implements Shape {
private Lib2Rectangle rectangle;
public RectangleAdapter(Lib2Rectangle rectangle) {
this.rectangle = rectangle;
}
// 实现目标接口的 draw 方法,调用新的绘制矩形方法
@Override
public void draw() {
rectangle.lib2DrawRectangle();
}
}
public class Main {
public static void main(String[] args) {
// 创建不同来源的图形对象
Lib1Circle lib1Circle = new Lib1Circle(5.0);
Lib2Rectangle lib2Rectangle = new Lib2Rectangle(3.0, 4.0);
// 创建适配器对象
Shape circleAdapter = new CircleAdapter(lib1Circle);
Shape rectangleAdapter = new RectangleAdapter(lib2Rectangle);
// 使用 List 管理 Shape 对象
List shapes = new ArrayList<>();
shapes.add(circleAdapter);
shapes.add(rectangleAdapter);
// 以统一的接口调用绘制方法
for (Shape shape : shapes) {
shape.draw();
}
}
}
Shape
****(目标接口):定义了一个抽象方法draw()
,它是客户端期望使用的统一接口。CircleAdapter
和RectangleAdapter
****(适配器):这两个类继承自Shape
接口,实现了draw()
方法。同时,它们分别持有Lib1Graphics::Circle
和Lib2Graphics::Rectangle
的引用,在draw()
方法中调用被适配者的特定绘制方法。Lib1Graphics::Circle
和Lib2Graphics::Rectangle
****(被适配者):它们是来自不同图形库的类,有各自独立的绘制方法,通过适配器类与Shape
接口建立联系。1. 提高代码复用性
适配器模式可以让现有的类在不改变其代码的情况下被复用。当需要使用一个已经存在的类,但其接口与当前系统不兼容时,通过创建适配器类,可以将这个类适配到新的接口上,从而避免了重新编写大量的代码。例如,在一个旧系统中已经有一个功能完善的算法类,但新系统需要的接口与该类的接口不同,此时可以创建一个适配器类来复用这个算法类。
2. 增强系统的灵活性和可扩展性
通过适配器模式,可以方便地对系统进行扩展。当有新的类需要集成到系统中,而其接口与现有系统不兼容时,只需要创建一个新的适配器类即可,不会影响到系统的其他部分。这使得系统能够更容易地适应变化,应对新的需求。例如,在一个图形绘制系统中,随着新的图形库的引入,只需要为新图形库创建适配器,就可以将其集成到现有系统中。
3. 解耦作用
适配器模式可以将客户端代码和被适配的类解耦。客户端只需要与目标接口进行交互,而不需要了解被适配类的具体实现细节。这样,当被适配类发生变化时,只要适配器类能够正确地进行适配,客户端代码就不需要进行修改。例如,在一个数据处理系统中,客户端只关心数据处理的结果,而不关心具体的数据来源和处理方式,适配器可以将不同来源的数据适配到统一的接口上,实现客户端与数据来源的解耦。
4. 符合开闭原则
开闭原则强调对扩展开放,对修改关闭。适配器模式很好地遵循了这一原则。在系统需要引入新的接口或类时,可以通过创建新的适配器类来实现,而不需要修改现有的客户端代码和被适配类的代码。例如,在一个电商系统中,随着支付方式的不断增加,只需要为新的支付方式创建适配器,就可以将其集成到系统中,而不需要修改原有的业务逻辑代码。
1. 增加系统复杂性
适配器模式引入了额外的适配器类,这会增加系统的代码量和复杂性。随着适配器数量的增加,系统的结构可能会变得更加复杂,理解和维护的难度也会相应提高。例如,在一个大型系统中,如果存在多个不同类型的适配器,可能会让开发人员在查找和理解代码时感到困惑。
2. 性能开销
在适配器模式中,适配器类需要进行接口转换和数据处理,这可能会带来一定的性能开销。尤其是在需要频繁进行适配操作的场景下,性能问题可能会更加明显。例如,在一个对性能要求极高的实时系统中,适配器的转换操作可能会影响系统的响应速度。
3. 过多使用可能导致设计混乱
如果在系统中过度使用适配器模式,可能会导致设计上的混乱。适配器模式本质上是一种补救措施,用于解决接口不兼容的问题。如果在设计阶段没有充分考虑接口的兼容性,而过多地依赖适配器来解决问题,会使系统的架构变得不清晰,难以把握系统的整体设计意图。例如,在一个小型项目中,如果频繁使用适配器来拼凑不同的功能模块,可能会导致项目的结构变得松散,难以维护和扩展。
1. 整合旧系统与新系统
2. 复用第三方库或组件
3. 适配不同的数据格式
4. 适配不同的平台或环境
结构型设计模式的核心目的是通过改变对象的组合方式,来实现系统的功能扩展和优化。它可以帮助开发者处理类和对象之间的关系,解决接口不兼容、代码复用、系统结构优化等问题,使系统的设计更加灵活、可维护和可扩展。常见结构型设计模式有:适配器模式、桥接模式、装饰器模式、外观模式、享元模式、代理模式等。 ↩︎