适配器模式(Adapter Pattern)是一种结构型设计模式,它能使接口不兼容的类能够相互合作。这种模式通过将一个类的接口转换成客户端期望的另一个接口,解决了因接口不匹配而导致的类无法一起工作的问题。

适配器模式的核心角色

  1. 目标接口(Target):客户端期望的接口,适配器将适配到这个接口
  2. 适配者(Adaptee):需要被适配的现有接口
  3. 适配器(Adapter):实现目标接口,并包装一个适配者对象,将适配者接口转换为目标接口

适配器模式的两种实现方式

1. 类适配器(使用继承)

通过多重继承同时继承目标接口和适配者类,实现接口转换。

#include <iostream>

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

// 适配者:需要被适配的类
class Adaptee {
public:
    void specificRequest() const {
        std::cout << "Adaptee: 特殊请求(需要被适配)" << std::endl;
    }
};

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

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

int main() {
    std::cout << "客户端直接使用目标对象:" << std::endl;
    Target* target = new Target();
    clientCode(target);

    std::cout << "\n客户端使用适配器:" << std::endl;
    Target* adapter = new ClassAdapter();
    clientCode(adapter);

    delete target;
    delete adapter;
    return 0;
}
    
2. 对象适配器(使用组合)

通过组合方式包含一个适配者对象,实现接口转换,这是更常用的方式。

#include <iostream>

// 目标接口
class Target {
public:
    virtual void request() const {
        std::cout << "Target: 标准请求" << std::endl;
    }
    virtual ~Target() = default;
};

// 适配者
class Adaptee {
public:
    void specificRequest() const {
        std::cout << "Adaptee: 特殊请求(需要被适配)" << std::endl;
    }
};

// 对象适配器:通过组合实现适配
class ObjectAdapter : public Target {
private:
    Adaptee* adaptee;  // 包含适配者对象

public:
    ObjectAdapter(Adaptee* adaptee) : adaptee(adaptee) {}

    void request() const override {
        // 调用适配者的特殊方法
        adaptee->specificRequest();
    }
};

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

int main() {
    std::cout << "客户端直接使用目标对象:" << std::endl;
    Target* target = new Target();
    clientCode(target);

    std::cout << "\n客户端使用对象适配器:" << std::endl;
    Adaptee* adaptee = new Adaptee();
    Target* adapter = new ObjectAdapter(adaptee);
    clientCode(adapter);

    delete target;
    delete adapter;
    delete adaptee;
    return 0;
}
    

类适配器与对象适配器的对比

  • 类适配器

    • 优点:结构简单,不需要额外的指针
    • 或引用
    • 缺点:使用多重继承,与适配者类绑定较紧,灵活性差
  • 对象适配器

    • 优点:采用组合方式,更符合合成复用原则,灵活性高,可适配不同的适配者子类
    • 缺点:需要额外的指针或引用管理适配者对象

在实际开发中,对象适配器更为常用,因为它耦合度更低,更灵活。

适配器模式的应用场景

  1. 集成第三方库或组件时,接口不匹配
  2. 重构旧系统时,需要让新代码与遗留代码协同工作
  3. 希望复用一些现存的类,但接口不符合当前系统的要求
  4. 统一多个类的接口,便于客户端统一调用

适配器模式的优缺点

优点

  • 使接口不兼容的类可以一起工作
  • 提高了类的复用性,无需修改原有代码
  • 灵活性和扩展性好,符合开放-封闭原则
  • 分离了目标接口和适配者的实现,降低耦合度

缺点

  • 增加了系统的复杂性,需要理解适配器的工作原理
  • 过多使用适配器可能使系统结构变得混乱
  • 对于类适配器,适配者类不能是最终类(final class)

适配器模式在C++标准库中也有应用,例如std::stackstd::queue就是基于std::deque的适配器,它们提供了不同的接口但内部使用了双端队列的功能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐