设计模式-adapter模式(适配器)

发布于:2025-02-25 ⋅ 阅读:(19) ⋅ 点赞:(0)

解释

适配器模式(Adapter Pattern)用于将一个类的接口转换成客户端所期望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。该模式属于结构型设计模式。

应用场景

场景1:旧系统与新系统的整合

当你有一个现有系统,并且想要集成一个第三方库或新的模块时,可能会遇到接口不匹配的问题。此时,适配器模式可以帮助你创建一个“桥梁”,使得旧代码能够调用新API,反之亦然。

场景2:不同第三方库之间的互操作性

在某些情况下,你可能需要同时使用多个来自不同供应商的库,但它们的API并不一致。适配器模式可以使这些库看起来具有相同的接口,从而简化了它们之间的交互。

场景3:遗留代码重构

当你对旧有的代码进行重构时,有时会发现一些类的设计不再符合当前的需求,或者引入了更好的实现方式。适配器模式可以用来逐步迁移系统,而不会立即废弃所有旧有逻辑。

不使用适配器模式的缺点

如果不使用适配器模式,在面对上述场景时,将会遇到以下问题:
代码重复:直接修改现有类以适应新需求会导致大量冗余代码。
耦合度高:紧密依赖特定实现,不利于维护和扩展。
难以维护:随着项目的发展,硬编码式的解决方案会变得越来越复杂难懂。
违反开闭原则:每当需要添加新的功能或支持新的接口时,都需要更改现有代码,而不是通过增加新的类来实现。

示例代码对比

1. 不使用适配器模式

假设我们有两个不同的图形绘制库 OldDrawingAPI 和 NewDrawingAPI,并且希望在同一个应用程序中使用它们。

// 旧版绘图API
class OldDrawingAPI {
public:
    void drawCircle(int x, int y, int radius) {
        // 实现...
        std::cout << "Using Old API to draw circle at (" << x << ", " << y << ") with radius " << radius << std::endl;
    }
};

// 新版绘图API
class NewDrawingAPI {
public:
    void renderCircle(const Point& center, float radius) {
        // 实现...
        std::cout << "Using New API to render circle at (" << center.x << ", " << center.y << ") with radius " << radius << std::endl;
    }
};

struct Point {
    int x, y;
};

// 应用程序逻辑 - 直接调用不同API
void drawCirclesWithoutAdapter() {
    OldDrawingAPI oldApi;
    NewDrawingAPI newApi;

    // 使用旧API画圆
    oldApi.drawCircle(10, 20, 5);

    // 使用新API画圆
    Point p{15, 25};
    newApi.renderCircle(p, 7.5f);
}

缺点分析:

• 需要在应用层面上处理两种不同风格的API调用。
• 如果将来要替换其中一个API,则需要修改大量现有代码。
• 违反了单一职责原则,因为应用程序同时承担了业务逻辑和接口适配的责任。

2. 使用适配器模式

为了解决以上问题,我们可以为 OldDrawingAPI 创建一个适配器,使其能够像 NewDrawingAPI 一样被使用。

// 定义统一接口
class IDrawingAPI {
public:
    virtual void drawCircle(const Point& center, float radius) = 0;
    virtual ~IDrawingAPI() = default;
};

// 新版绘图API适配器
class NewDrawingAPIAdapter : public IDrawingAPI {
private:
    NewDrawingAPI* api;
public:
    NewDrawingAPIAdapter(NewDrawingAPI* napi) : api(napi) {}
    void drawCircle(const Point& center, float radius) override {
        api->renderCircle(center, radius);
    }
};

// 旧版绘图API适配器
class OldDrawingAPIAdapter : public IDrawingAPI {
private:
    OldDrawingAPI* api;
public:
    OldDrawingAPIAdapter(OldDrawingAPI* oapi) : api(oapi) {}
    void drawCircle(const Point& center, float radius) override {
        api->drawCircle(center.x, center.y, static_cast<int>(radius));
    }
};

// 应用程序逻辑 - 统一调用接口
void drawCirclesWithAdapter() {
    OldDrawingAPI oldApi;
    NewDrawingAPI newApi;

    // 创建适配器实例
    IDrawingAPI* oldApiAdapter = new OldDrawingAPIAdapter(&oldApi);
    IDrawingAPI* newApiAdapter = new NewDrawingAPIAdapter(&newApi);

    // 现在可以统一调用了
    Point p{10, 20};
    oldApiAdapter->drawCircle(p, 5.0f);
    newApiAdapter->drawCircle({15, 25}, 7.5f);

    delete oldApiAdapter;
    delete newApiAdapter;
}

// 使用智能指针改进资源管理
void drawCirclesWithAdapterSmartPtr() {
    OldDrawingAPI oldApi;
    NewDrawingAPI newApi;

    // 使用智能指针管理适配器生命周期
    std::unique_ptr<IDrawingAPI> oldApiAdapter(new OldDrawingAPIAdapter(&oldApi));
    std::unique_ptr<IDrawingAPI> newApiAdapter(new NewDrawingAPIAdapter(&newApi));

    // 统一调用
    Point p{10, 20};
    oldApiAdapter->drawCircle(p, 5.0f);
    newApiAdapter->drawCircle({15, 25}, 7.5f);
}

优点分析:

一致性:所有绘图操作都通过同一接口完成,提高了代码可读性和可维护性。
灵活性:新增加其他绘图库只需要编写对应的适配器即可,无需改动已有代码。
遵循开闭原则:当需要支持更多类型的绘图API时,只需扩展适配器,而不需要修改客户端代码。
降低耦合:将具体实现细节封装到适配器内部,减少了外部依赖关系。

总结

适配器模式不仅解决了接口兼容性的问题,还增强了系统的灵活性和可维护性。通过定义一个统一的接口,它可以隐藏底层实现的具体差异,使高层模块更加关注业务逻辑而非技术细节。此外,采用智能指针等现代C++特性还可以进一步提升资源管理和代码安全性。