设计模式:适配器模式(Adapter)

适配器模式是一种结构型设计模式,它允许不兼容的接口之间进行协作。适配器模式就像现实世界中的电源适配器一样,可以让不同规格的插头互相兼容。

基本概念

适配器模式主要解决接口不兼容的问题,它通过将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。

三种角色

  1. 目标接口(Target):客户期望的接口

  2. 适配者(Adaptee):需要被适配的现有接口

  3. 适配器(Adapter):将Adaptee适配成Target接口的类

两种实现方式

1. 类适配器(通过继承实现)

#include <iostream>

// 目标接口(客户期望的接口)
class Target {
public:
    virtual ~Target() = default;
    virtual void request() const {
        std::cout << "Target: 标准请求\n";
    }
};

// 需要适配的类(现有接口)
class Adaptee {
public:
    void specificRequest() const {
        std::cout << "Adaptee: 特殊请求\n";
    }
};

// 类适配器(通过继承Adaptee)
class Adapter : public Target, private Adaptee {
public:
    void request() const override {
        // 调用Adaptee的方法
        specificRequest();
    }
};

// 客户端代码
void clientCode(const Target* target) {
    target->request();
}

int main() {
    std::cout << "使用标准目标对象:\n";
    Target* target = new Target;
    clientCode(target);
    std::cout << "\n";

    std::cout << "使用适配器:\n";
    Adapter* adapter = new Adapter;
    clientCode(adapter);
    std::cout << "\n";

    delete target;
    delete adapter;
    
    return 0;
}

2. 对象适配器(通过组合实现)

#include <iostream>
#include <memory>

// 目标接口(客户期望的接口)
class Target {
public:
    virtual ~Target() = default;
    virtual void request() const {
        std::cout << "Target: 标准请求\n";
    }
};

// 需要适配的类(现有接口)
class Adaptee {
public:
    void specificRequest() const {
        std::cout << "Adaptee: 特殊请求\n";
    }
};

// 对象适配器(通过组合Adaptee)
class Adapter : public Target {
private:
    std::unique_ptr<Adaptee> adaptee_;
public:
    Adapter(Adaptee* adaptee) : adaptee_(adaptee) {}
    
    void request() const override {
        // 调用Adaptee的方法
        adaptee_->specificRequest();
    }
};

// 客户端代码
void clientCode(const Target* target) {
    target->request();
}

int main() {
    std::cout << "使用标准目标对象:\n";
    Target* target = new Target;
    clientCode(target);
    std::cout << "\n";

    std::cout << "使用适配器:\n";
    Adapter* adapter = new Adapter(new Adaptee);
    clientCode(adapter);
    std::cout << "\n";

    delete target;
    delete adapter;
    
    return 0;
}

适配器模式的应用场景

  1. 遗留系统集成:当需要使用现有的类,但其接口与你的代码不兼容时

  2. 第三方库适配:当需要使用第三方库但接口不符合你的需求时

  3. 接口版本兼容:不同版本的接口需要一起工作时

  4. 统一接口:多个类有相似功能但接口不同,需要统一接口时

适配器模式的优缺点

优点

  • 单一职责原则:可以将接口转换代码与业务逻辑分离

  • 开闭原则:可以在不修改现有代码的情况下引入新的适配器

  • 提高了类的复用性

  • 增加了类的透明度

缺点

  • 增加了系统的复杂性

  • 有时需要牺牲一些性能(因为多了一层调用)

C++中的实际应用示例

STL中的适配器

C++标准模板库(STL)中有许多适配器的例子:

  1. 容器适配器

    • stack适配了dequelist

    • queue适配了dequelist

    • priority_queue适配了vectordeque

#include <iostream>
#include <stack>
#include <vector>

int main() {
    // stack是适配器,默认使用deque作为底层容器
    std::stack<int> s1;
    
    // 也可以指定底层容器为vector
    std::stack<int, std::vector<int>> s2;
    
    s1.push(1);
    s1.push(2);
    s1.push(3);
    
    while (!s1.empty()) {
        std::cout << s1.top() << " ";
        s1.pop();
    }
    
    return 0;
}
  1. 迭代器适配器

    • reverse_iterator

    • back_insert_iterator

    • front_insert_iterator

函数对象适配器

C++11之前的标准库中有bind1stbind2nd等函数适配器:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    
    // 使用bind2nd适配器将less转换为判断小于3的函数
    auto it = std::find_if(v.begin(), v.end(), 
                          std::bind2nd(std::less<int>(), 3));
    
    if (it != v.end()) {
        std::cout << "First element less than 3: " << *it << std::endl;
    }
    
    return 0;
}

在现代C++中,可以使用lambda表达式替代这些适配器:

auto it = std::find_if(v.begin(), v.end(), [](int x) { return x < 3; });

适配器模式与其它模式的关系

  • 桥接模式:通常在设计初期使用,而适配器模式通常在已有系统中使用

  • 装饰器模式:不改变接口但增强了功能,适配器则改变接口

  • 代理模式:保持接口不变,适配器则改变接口

  • 外观模式:为一组接口提供简化接口,适配器则尝试使单个接口可用

总结

     适配器模式是C++中非常有用的设计模式,特别是在集成现有代码或第三方库时。通过适配器,可以让不兼容的接口协同工作,而不需要修改原有代码。C++中的类适配器和对象适配器提供了两种不同的实现方式,各有优缺点。STL中的许多组件也使用了适配器模式的思想,这证明了它在实际开发中的实用性。

在实际开发中,应当根据具体情况选择使用适配器模式,权衡其带来的灵活性和增加的复杂性。

生活中的"万能转换插头"

生活比喻:国际旅行中的电源适配器

想象你要出国旅行,带了手机、电脑等电子设备,但到了目的地发现:

  • 你的设备插头是两脚扁型(中国标准)

  • 酒店的插座是三脚圆型(英国标准)

这时候你该怎么办?直接硬插肯定不行,你有三个选择:

  1. 改造设备:把设备拆开改造成英国插头(风险高,不现实)

  2. 改造酒店:让酒店换插座(几乎不可能)

  3. 使用转换插头:买一个适配器,完美解决问题

这个转换插头就是适配器模式的完美体现!

编程世界的适配器

在软件开发中同样存在这种"接口不匹配"的情况:

  • 老系统是用C语言写的,新系统用C++

  • 第三方库的接口和你的系统不兼容

  • 不同团队开发的模块接口标准不一致

适配器模式就是软件界的"转换插头",让你无需修改现有代码就能让不兼容的接口协同工作。

通俗总结

适配器模式的本质就是:当你想用A插头插B插座时,不是去改造A或B,而是找一个转换器让它们能连上

它的三大特点:

  1. 兼容性:让新旧/不同标准的接口能一起工作

  2. 非侵入性:不修改原有代码

  3. 灵活性:可以随时增加新的适配器支持更多接口

现实中的其他例子
  1. 读卡器:把SD卡转换成USB接口

  2. 翻译官:在中英会谈中充当"语言适配器"

  3. 转接线:把Type-C接口转换成3.5mm耳机孔

  4. 货币兑换:把美元"适配"成人民币使用

什么时候该用适配器模式?

当系统出现以下情况时:

  • 想用现成的类,但接口不匹配

  • 需要集成多个不兼容的子系统

  • 想创建一个可复用的类来与未知类协作

  • 需要使用第三方库但接口不符合需求

记住适配器模式的口诀:不匹配,加适配;不改源码,不伤和气!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值