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

news/2025/2/24 18:22:58

解释

适配器模式(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++特性还可以进一步提升资源管理和代码安全性。


http://www.niftyadmin.cn/n/5864697.html

相关文章

从人机环境系统智能角度看传统IP的全球化二次创作法则

从人机环境系统智能的视角看&#xff0c;传统IP的全球化二次创作法则需结合技术、文化、伦理与环境的复杂协同。这一过程不仅是内容的本土化改编&#xff0c;更是人、机器与环境在动态交互中实现价值共创的体现。 一、人机环境系统智能的底层逻辑与IP二次创作的融合 1、感知层&…

Go 语言中的协程

概念 Go语言中的协程&#xff08;Goroutine&#xff09;是一种由Go运行时管理的轻量级线程。它是Go语言并发模型的核心&#xff0c;旨在通过简单、易用的方式支持高并发的程序设计。 创建协程 协程的创建非常简单&#xff0c;只需要使用go关键字&#xff0c;后面跟着一个函数…

深度学习-4.优化与正则化

Deep Learning - Lecture 4 Optimization and Regularization 优化&#xff08;Optimization&#xff09;随机梯度下降(Stochastic gradient descent)带动量的随机梯度下降法&#xff08;Stochastic gradient descent (with momentum)&#xff09;自适应梯度方法&#xff08;Ad…

网络安全研究

1.1 网络安全面临的威胁 网络安全面临的威胁呈现出多样化和复杂化的趋势&#xff0c;给个人、企业和国家的安全带来了严峻挑战。以下是当前网络安全面临的主要威胁&#xff1a; 1.1.1 数据泄露风险 数据泄露是当前网络安全的重大威胁之一。根据国家互联网应急中心发布的《20…

500字理透react的hook闭包问题

在react中hook的闭包问题很容易在不经意间犯错&#xff0c;项目写大了之后更是难以找到到底是哪里出了问题。 为什么会出现闭包问题 出现闭包问题的原因就是函数中操作的变量不是最新的变量&#xff0c;什么意思呢&#xff0c;我们知道函数组件每次刷新都是重新运行一次函数&…

Ollama 运行模型

Ollama 运行模型使用 ollama run 命令。 例如要运行 Llama 3.2 并与该模型对话可以使用以下命令&#xff1a; ollama run llama3.2 执行以上命令如果没有该模型会去下载 llama3.2 模型&#xff1a; 等待下载完成后&#xff0c;用户在终端中&#xff0c;输入以下命令来加载 L…

vscode无法预览Markdown在线图片链接

问题&#xff1a;在VSCode中&#xff0c;打开MarkDown文件&#xff0c;存在在线图片链接&#xff0c; 但是在预览时却无法显示。 原因&#xff1a;因为Visual Studio Code中的MarkDown默认配置中只允许载入安全内容 解决方法&#xff1a; 1、输入快捷键 Ctrl Shift P 打开…

【数据结构】(11) Map 和 Set

一、Map 和 Set 的简介 1、Set 和 Map Map 和 Set 是集合类框架学习的最后一部分。Map 和 Set 都是接口&#xff0c;需要通过 TreeSet、HashSet 和 TreeMap、HashMap 实例化。注意&#xff0c;Set 实现了 Collection&#xff0c;Map 并没有。 Set 存放的是键&#xff08;Key&a…