C++【适配器模式】

文章详细介绍了适配器模式,如何通过创建适配器解决不同接口之间的兼容问题,如将JSON库转换为接受XML数据。UML图展示了客户端、接口和适配器的角色。适配器模式适用于代码与遗留系统间的转换,强调了其在已有代码中的灵活性和单一职责原则的应用。

简单介绍

适配器模式是一种结构型设计模式 | 它能使接口不兼容的对象能够相互合作。(是适配各种不同接口的一个中间件)

基础理解

举个例子:当你引用了一个第三方数据分析库,但这个库的接口只能兼容JSON 格式的数据。但你需要它能接收核心类处理后产生的 XML 文件。
而且因为这个库不是你写的,所以你不能对其源码进行修改成接收XML格式的文件。

你无法 “直接” 使用分析函数库, 因为它所需的输入数据格式与你的程序不兼容。

解决:这时候你就可以创建一个适配器。能够在转换对象的接口,将XML 格式转换为JSON 格式。你甚至可以创建一个双向适配器来实现双向转换调用来实现不通过接口对象之间的合作。

适配器不仅可以转换不同格式的数据, 其还有助于采用不同接口的对象之间的合作。

在这里插入图片描述

UML图

  1. 客户端 (Client) 是包含当前程序业务逻辑的类。

  2. 客户端接口 (Client Interface) 描述了其他类与客户端代码合作时必须遵循的协议。

  3. 服务 (Service) 中有一些功能类 (通常来自第三方或遗留系统)。 客户端与其接口不兼容, 因此无法直接调用其功能。

  4. 适配器 (Adapter) 是一个可以同时与客户端和服务交互的类: 它在实现客户端接口的同时封装了服务对象。
    在这里插入图片描述

客户端代码只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。

类适配器:适配器同时继承两个对象的接口(仅能在支持多重继承的编程语言中实现 c++)

类适配器只是继承了客户端接口和服务 的行为。将适配功能用重写的方式完成

实现方式

确保至少有两个类的接口不兼容:

  1. 一个无法修改 (通常是第三方、 遗留系统或者存在众多已有依赖的类) 的功能性服务类。
  2. 一个或多个将受益于使用服务类的客户端类。
#include <iostream>
#include <stdlib.h>

// 双端队列(已有实现的类和接口,第三方类等等)
class Deque
{
public:
    void push_back(int x) { std::cout << "Deque push_back" << std::endl; }
    void push_front(int x) { std::cout << "Deque push_front" << std::endl; }
    void pop_back() { std::cout << "Deque pop_back" << std::endl; }
    void pop_front() { std::cout << "Deque pop_front" << std::endl; }
};
// 声明客户端接口, 描述客户端如何与服务交互。

// 顺序容器(客户端期望的类和接口)
class Sequence
{
public:
    virtual void push(int x) = 0;
    virtual void pop() = 0;
};

// 栈(适配器)
class Stack : public Sequence
{
public:
    Stack(Deque *deque) : deque_(deque) {}
    // 依次实现适配器类客户端接口的所有方法。 适配器会将实际工作委派给服务对象, 自身只负责接口或数据格式的转换。
    void push(int x) { deque_->push_back(x); }
    void pop() { deque_->pop_back(); }

private:
    Deque *deque_;
    /* 双端队列
    在适配器类中添加一个成员变量用于保存对于服务对象的引用。
    通常情况下会通过构造函数对该成员变量进行初始化, 会更加灵活
    但有时在调用其方法时将该变量直接传递给适配器会更方便。*/
};
// 队列(适配器)
class Queue : public Sequence
{
public:
    void push(int x) { deque.push_back(x); }
    void pop() { deque.pop_front(); }

private:
    Deque deque; // 上述注释提到的:将该变量直接传递给适配器
};

int main()
{
    Deque *deque = new Deque();
    // 客户端必须通过客户端接口使用适配器。 这样一来, 你就可以在不影响客户端代码的情况下修改或扩展适配器。
    Sequence *s1 = new Stack(deque); // 客户期望的stack
    Sequence *s2 = new Queue();      // 客户期望的queue

    s1->push(1);
    s1->pop();

    s2->push(1);
    s2->pop();

    delete s1;
    delete s2;
    system("pause");
    return 0;
}

适合场景

  1. 可作为代码与遗留类、 第三方类或提供怪异接口的类之间的转换器。
  2. 假设有一个继承体系,其中有一个基类,以及多个子类 ,它们都有一些共同的方法,但不是所有子类都有额外的一些方法。现在您希望为这些额外的方法提供统一的接口给客户端使用。

创建一个适配器类 Adapter,实现客户端所期望的接口,然后将 基类的实例作为适配器类的成员变量。在适配器类中,您可以根据需要调用子类中的方法,并实现额外的方法。与装饰模式类似

与其他模式的关系

  1. 桥接模式通常会于开发前期进行设计, 使你能够将程序的各个部分独立开来以便开发。 另一方面, 适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作。

  2. 适配器可以修改已有对象的接口, 装饰模式则能在不改变对象接口的前提下强化对象功能。

  3. 适配器能为被封装对象提供不同的接口, 代理模式能为对象提供相同的接口, 装饰则能为对象提供加强的接口。

  4. 外观模式为现有对象定义了一个新接口, 适配器则会试图运用已有的接口。 适配器通常只封装一个对象, 外观通常会作用于整个对象子系统上。

桥接、 状态模式和策略模式 (在某种程度上包括适配器) 模式的接口非常相似。 实际上, 它们都基于组合模式——将工作委派给其他对象, 不过也各自解决了不同的问题。

模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。

优缺点

优点缺点
单一职责原则你可以将接口或数据转换代码从程序主要业务逻辑中分离。代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。
开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值