【第七节】C++设计模式(结构型模式)-Adapter(适配器)模式

目录

一、问题背景

二、适配器模式的两种类型

三、实现代码

四、总结讨论


一、问题背景

适配器模式:解决接口不兼容问题的桥梁        

        在生活中,我们经常会遇到这样的场景:假设你买了一台新款笔记本电脑,它只有 USB-C 接口,但你手头的设备(如鼠标、U 盘、显示器)都是传统的 USB-A 接口。为了让这些设备能够在新电脑上正常工作,你有两种选择:

        (1)直接替换:将所有旧设备换成支持 USB-C 的新设备。这种方式虽然直接,但成本高且不现实。
        (2)使用适配器:购买一个 USB-C 转 USB-A 的适配器,将旧设备连接到新电脑上。适配器就像一个中间人,将 USB-A 接口转换为 USB-C 接口,让新旧设备能够协同工作。

        适配器模式的作用类似于这个 USB 适配器。它能够将一个类的接口转换为客户端期望的接口,从而解决接口不兼容的问题。

        类似的问题在软件开发中也十分常见。例如,为了加快开发进度,我们可能会引入第三方库,但这个库的接口与应用程序中设计的接口不一致。为了让这些不兼容的类能够协同工作,适配器模式(Adapter Pattern)应运而生。它能够将一个类(第三方库)的接口转换为客户期望的接口。

二、适配器模式的两种类型

适配器模式分为两种类型,分别对应上述生活中的两种解决方案:

(1)类适配器模式:通过继承的方式复用现有接口。
(2)对象适配器模式:通过组合的方式复用现有接口。

模式结构
适配器模式的典型结构如图所示:

Adapter Pattern(类模式)结构图

 Adapter Pattern(对象模式)结构图

类适配器模式:适配器通过继承目标接口和现有接口,将两者结合起来。
对象适配器模式:适配器通过组合现有接口的实例,实现目标接口的功能。

三、实现代码

以下是适配器模式的完整实现代码(使用C++编写)。

类适配器模式
代码片段 1:Adapter.h

// Adapter.h
#ifndef _ADAPTER_H_      // 防止头文件被多次包含
#define _ADAPTER_H_

// 目标接口:定义客户端期望的接口
class Target {
public:
    Target() = default;                     // 默认构造函数
    virtual ~Target() = default;            // 虚析构函数,确保派生类正确释放资源
    virtual void Request() = 0;             // 纯虚函数,定义目标接口
};

// 被适配者:已存在的类,其接口与目标接口不兼容
class Adaptee {
public:
    Adaptee() = default;                    // 默认构造函数
    ~Adaptee() = default;                   // 默认析构函数
    void SpecificRequest();                 // 具体请求方法,与目标接口不兼容
};

// 类适配器:通过继承 Target 和 Adaptee 实现接口适配
class Adapter : public Target, private Adaptee {
public:
    Adapter() = default;                    // 默认构造函数
    ~Adapter() override = default;          // 默认析构函数
    void Request() override;                 // 实现目标接口,委托给 Adaptee 的具体方法
};

#endif //~_ADAPTER_H_

代码片段 2:Adapter.cpp

// Adapter.cpp
#include "Adapter.h"
#include <iostream>

// Target 的默认实现
Target::Target() = default;
Target::~Target() = default;

// Adaptee 的默认实现
Adaptee::Adaptee() = default;
Adaptee::~Adaptee() = default;

// Adaptee 的具体请求方法
void Adaptee::SpecificRequest() {
    std::cout << "Adaptee::SpecificRequest" << std::endl;
}

// Adapter 的默认实现
Adapter::Adapter() = default;
Adapter::~Adapter() = default;

// Adapter 的 Request 实现:委托给 Adaptee 的 SpecificRequest
void Adapter::Request() {
    this->SpecificRequest();  // 调用 Adaptee 的方法
}

对象适配器模式
代码片段 1:Adapter.h

// Adapter.h
#ifndef _ADAPTER_H_      // 防止头文件被多次包含
#define _ADAPTER_H_

// 目标接口:定义客户端期望的接口
class Target {
public:
    Target() = default;                     // 默认构造函数
    virtual ~Target() = default;            // 虚析构函数,确保派生类正确释放资源
    virtual void Request() = 0;             // 纯虚函数,定义目标接口
};

// 被适配者:已存在的类,其接口与目标接口不兼容
class Adaptee {
public:
    Adaptee() = default;                    // 默认构造函数
    ~Adaptee() = default;                   // 默认析构函数
    void SpecificRequest();                 // 具体请求方法,与目标接口不兼容
};

// 对象适配器:通过组合 Adaptee 实现接口适配
class Adapter : public Target {
public:
    explicit Adapter(Adaptee* ade);         // 显式构造函数,接受 Adaptee 对象指针
    ~Adapter() override;                    // 析构函数
    void Request() override;                // 实现目标接口,委托给 Adaptee 的具体方法
private:
    Adaptee* _ade;                          // 指向 Adaptee 对象的指针
};

#endif //~_ADAPTER_H_

代码片段 2:Adapter.cpp

// Adapter.cpp
#include "Adapter.h"
#include <iostream>

// Target 的默认实现
Target::Target() = default;
Target::~Target() = default;

// Adaptee 的默认实现
Adaptee::Adaptee() = default;
Adaptee::~Adaptee() = default;

// Adaptee 的具体请求方法
void Adaptee::SpecificRequest() {
    std::cout << "Adaptee::SpecificRequest" << std::endl;
}

// Adapter 的构造函数:初始化 Adaptee 指针
Adapter::Adapter(Adaptee* ade) : _ade(ade) {}

// Adapter 的析构函数
Adapter::~Adapter() {
    delete _ade;  // 释放 Adaptee 对象
}

// Adapter 的 Request 实现:委托给 Adaptee 的 SpecificRequest
void Adapter::Request() {
    _ade->SpecificRequest();  // 调用 Adaptee 的方法
}

代码片段 3:main.cpp

// main.cpp
#include "Adapter.h"
#include <iostream>

int main(int argc, char* argv[]) {
    // 创建被适配者对象
    Adaptee* ade = new Adaptee();

    // 创建适配器对象,并传入被适配者对象
    Target* adt = new Adapter(ade);

    // 调用目标接口,实际执行的是 Adaptee 的方法
    adt->Request();

    // 释放资源
    delete adt;  // adt 的析构函数会释放 ade
    return 0;
}

代码说明
        适配器模式的实现相对简单,但其中有两个重要概念需要明确:

        (1)接口继承:子类通过继承获得父类的接口。
        (2)实现继承:子类通过继承获得父类的实现。

        在类适配器模式中,适配器通过`private`继承`Adaptee`获得实现继承的效果,同时通过`public`继承`Target`获得接口继承的效果。而在对象适配器模式中,适配器通过组合`Adaptee`的实例来实现目标接口的功能。

四、总结讨论

        适配器模式的核心在于解决接口不兼容的问题。它通过以下两种方式实现:
        类适配器模式:通过多重继承实现接口适配。
        对象适配器模式:通过对象组合实现接口适配。

        在C++中,`public`继承既是接口继承又是实现继承,而`private`继承则仅用于实现继承。通过纯抽象基类可以模拟接口继承,但其效果不如Java中的`interface`纯粹。

        适配器模式不仅能够解决接口不兼容的问题,还能够提高代码的复用性和系统的灵活性,是面向对象设计中不可或缺的重要模式。

以下是适配器模式的主要优缺点:

优点

(1)解决接口不兼容问题:
        适配器模式的核心目标是让两个不兼容的接口能够协同工作,从而避免对现有代码的修改。

(2)提高代码复用性:
        通过适配器,可以复用已有的类或第三方库,而无需修改其源代码。

(3)符合开闭原则:
        适配器模式在不修改现有代码的情况下扩展系统功能,符合“对扩展开放,对修改关闭”的开闭原则。

(4)灵活性和可扩展性:
        可以轻松添加新的适配器来支持新的接口或功能,而不会影响现有的适配器或客户端代码。

(5)解耦:
        适配器模式将客户端与被适配者解耦,客户端只需要依赖目标接口,而不需要了解被适配者的具体实现。

(6)支持多种适配方式:
        适配器模式分为类适配器和对象适配器两种实现方式,开发者可以根据需求选择合适的方式。

缺点

(1)增加复杂性:
        引入适配器会增加类的数量,导致系统结构变得更加复杂,特别是在需要适配多个类时。

(2)过度使用会导致代码难以理解:
        如果系统中大量使用适配器模式,可能会导致代码的可读性和可维护性下降,因为需要跟踪多个适配器和被适配者之间的关系。

(3)性能开销:
        在对象适配器模式中,由于需要通过组合调用被适配者的方法,可能会引入额外的间接调用,导致一定的性能开销。

(4)不适合大规模重构:
        如果系统中存在大量的接口不兼容问题,使用适配器模式可能会导致适配器类过多,此时可能需要考虑更彻底的重构方案。

(5)可能违背单一职责原则:
        适配器类既需要实现目标接口,又需要调用被适配者的方法,可能会承担过多的职责,违背单一职责原则。

适用场景
适配器模式在以下场景中特别有用:
(1)集成第三方库:当需要使用第三方库,但其接口与现有系统不兼容时。
(2)复用旧代码:在系统升级或重构时,需要复用旧的类或模块,但其接口与新系统不匹配。
(3)统一接口:当需要为多个具有不同接口的类提供统一的接口时。
(4)解耦:当需要将客户端与具体的实现类解耦,以降低系统的耦合度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮7号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值