适配器模式(Adapter)

2023-11-03

适配器是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。又称封装器模式(Wrapper)。

适配器模式通过封装对象将复杂的转换过程隐藏于幕后。被封装的对象甚至察觉不到适配器的存在。例如,你可以使用一个将所有数据转换为英制单位(如英尺和英里)的适配器封装运行于米和千米单位制中的对象。
适配器不仅可以转换不同格式的数据,其还有助于采用不同接口的对象之间的合作。它的运作方式如下:

  • 适配器实现与其中一个现有对象兼容的接口。

  • 现有对象可以使用该接口安全地调用适配器方法。

  • 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。
    在这里插入图片描述

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

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

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

  • 适配器(Adapter) 是一个可以同时与客户端和服务交互的类:它在实现客户端接口的同时封装了服务对象。适配器接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。

客户端代码只需通过接口与适配器交互即可,无需与具体的适配器类耦合。因此,你可以向程序中添加新类型的适配器而无需修改已有代码。这在服务类的接口被更改或替换时很有用:你无需修改客户端代码就可以创建新的适配器类。

实现方式

  1. 确保至少有两个类的接口不兼容:
  • 一个无法修改(通常是第三方、遗留系统或者存在众多已有依赖的类)的功能性服务类。
  • 一个或多个将受益于使用服务类的客户端类。
  1. 声明客户端接口,描述客户端如何与服务交互。
  2. 创建遵循客户端接口的适配器类。所有方法暂时都为空。
  3. 在适配器类中添加一个成员变量用于保存对于服务对象的引用。通常情况下会通过构造函数对该成员变量进行初始化,但有时在调用其方法时将该变量传递给适配器会更方便。
  4. 依次实现适配器类客户端接口的所有方法。适配器会将实际工作委派给服务对象,自身只负责接口或数据格式的转换。
  5. 客户端必须通过客户端接口使用适配器。这样一来,你就可以在不影响客户端代码的情况下修改或扩展适配器。

优点

  • 单一职责原则你可以将接口或数据转换代码从程序主要业务逻辑中分离。
  • 开闭原则。只要客户端代码通过客户端接口与适配器进行交互,你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。

缺点

代码整体复杂度增加,因为你需要新增一系列接口和类。有时直接更改服务类使其与其他代码兼容会更简单。

与其他模式的关系

  1. 桥接通常会于开发前期进行设计,使你能够将程序的各个部分独立开来以便开发。另一方面,适配器通常在已有程序中使用,让相互不兼容的类能很好地合作。
  2. 适配器可以对已有对象的接口进行修改,装饰则能在不改变对象接口的前提下强化对象功能。此外,装饰还支持递归组合,适配器则无法实现。
    适配器能为被封装对象提供不同的接口,代理能为对象提供相同的接口,装饰则能为对象提供加强的接口。
  3. 外观为现有对象定义了一个新接口,适配器则会试图运用已有的接口。适配器通常只封装一个对象,外观通常会作用于整个对象子系统上。
  4. 桥接、状态和策略(在某种程度上包括适配器)模式的接口非常相似。实际上, 它们都基于组合模式——即将工作委派给其他对象,不过也各自解决了不同的问题。模式并不只是以特定方式组织代码的配方,你还可以使用它们来和其他开发者讨论模式所解决的问题。

示例一

方钉和圆孔的问题,我们要做的是让方钉适配圆孔。
适配器让方钉假扮成一个圆钉(RoundPeg),其半径等于方钉(SquarePeg)横截面对角线的一半(即能容纳方钉的最小外接圆的半径)。

// ClientInterface.h
#ifndef CLIENT_INTERFACE_H_
#define CLIENT_INTERFACE_H_

// 圆钉: 客户端接口, 在C++中定义成抽象基类
class RoundPeg {
 public:
    RoundPeg() {}
    virtual int get_radius() = 0;
};

#endif  // CLIENT_INTERFACE_H_

Adapter.h:

// Adapter.h:
#ifndef ADAPTER_H_
#define ADAPTER_H_

#include <cmath>
#include "Service.h"
#include "ClientInterface.h"

// 方钉适配器: 该适配器能让客户端将方钉放入圆孔中
class SquarePegAdapter : public RoundPeg {
 public:
    explicit SquarePegAdapter(SquarePeg* sp) : square_peg_(sp) {}
    int get_radius() override {
        return square_peg_->get_width() * sqrt(2) / 2;
    }

 private:
    SquarePeg* square_peg_;
};

#endif  // ADAPTER_H_

Service.h:

#ifndef SERVICE_H_
#define SERVICE_H_

// 方钉: 适配者类, 即和客户端不兼容的类
class SquarePeg {
 public:
    explicit SquarePeg(int w) : width_(w) {}
    int get_width() {
        return width_;
    }

 private:
    int width_;
};

#endif  // SERVICE_H_

Client.h:

#ifndef CLIENT_H_
#define CLIENT_H_

#include "ClientInterface.h"

// 圆孔: 客户端类
class RoundHole {
 public:
    explicit RoundHole(int r) : radius_(r) {}
    int get_radius() {
        return radius_;
    }
    bool isFit(RoundPeg* rp) {
        return radius_ >= rp->get_radius();
    }

 private:
    int radius_;
};

#endif  // CLIENT_H_

main.cpp

#include <iostream>
#include "Client.h"
#include "Adapter.h"

int main() {
    // 半径为10的圆孔
    RoundHole* hole = new RoundHole(10);

    // 半径分别为5和20的大小方钉 + 它们的适配器
    SquarePeg* samll_square_peg = new SquarePeg(5);
    SquarePeg* large_square_peg = new SquarePeg(20);
    SquarePegAdapter* small_square_peg_adapter = new SquarePegAdapter(samll_square_peg);
    SquarePegAdapter* large_square_peg_adapter = new SquarePegAdapter(large_square_peg);

    // hole->isFit(samll_square_peg);  // 编译报错
    // hole->isFit(large_square_peg);  / / 编译报错
    if (hole->isFit(small_square_peg_adapter)) {
        std::cout << "small square peg fits the hole" << std::endl;
    } else {
        std::cout << "small square peg don't fit the hole" << std::endl;
    }
    if (hole->isFit(large_square_peg_adapter)) {
        std::cout << "large square peg fits the hole" << std::endl;
    } else {
        std::cout << "large square peg don't fit the hole" << std::endl;
    }
}

编译运行

$g++ -g main.cpp -o adapter -std=c++11
$./adapter
small square peg fits the hole
large square peg don't fit the hole

示例二

假设,现在有现有的各种动物,都是现成的,有猫、狗。他们都有各自的方法比如说,卧倒,打滚等,但是呢,他们的接口又不一样。为了使主人去让猫、狗做动作的接口一致,就需要适配器。因为语言不通,一个喵喵喵,一个汪汪汪。

adpter.h

#include <iostream>

class DOG {
 public:
  void Dog_Bedding() { std::cout << "小狗子,请卧倒" << std::endl; }

  void Dog_Roll() { std::cout << "小狗子,请打滚" << std::endl; }
};

class CAT {
 public:
  void Cat_Bedding() { std::cout << "小猫咪,请卧倒" << std::endl; }

  void Cat_Roll() { std::cout << "小猫咪,请打滚" << std::endl; }
};

class Target {
 public:
  virtual void Bedding()=0;  // 外部符号
  virtual void Roll()=0;
};

class adpter_cat : public Target {
 public:
  adpter_cat(CAT* c) : cat_(c) {}
  void Bedding() { cat_->Cat_Bedding(); }
  void Roll() { cat_->Cat_Roll(); }

 private:
  CAT* cat_;
};

class adpter_dog : public Target {
 public:
  adpter_dog(DOG* d) : dog_(d) {}
  void Bedding() { dog_->Dog_Bedding(); }
  void Roll() { dog_->Dog_Roll(); }

 private:
  DOG* dog_;
};

main.cpp

// #include "Adpter_cat.h"
// #include "Adpter_dog.h"
// #include "cat.h"
// #include "dog.h"

#include "adpter.h"

int main() {
  CAT* cat = new CAT();
  DOG* dog = new DOG();
  adpter_cat ac(cat);
  adpter_dog ad(dog);

  ac.Bedding();
  ac.Roll();
  ad.Bedding();
  ad.Roll();
}

运行结果

小猫咪,请卧倒
小猫咪,请打滚
小狗子,请卧倒
小狗子,请打滚
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

适配器模式(Adapter) 的相关文章

  • c++享元模式

    享元模式 1 享元模式简介 享元模式在 设计模式 可复用面向对象软件的基础 一书中是这样说的 运用共享技术有效地支持大量细粒度的对象 本质就是对大量细粒度的对象进行共享 不是每个对象都要通过new的方式去创建 而是通过区分对象的内部状态和外
  • 设计模式之(三)---工厂方法模式

    女娲补天的故事大家都听过吧 这个故事是说 女娲在补了天后 下到凡间一看 哇塞 风景太优美了 天空是湛 蓝的 水是清澈的 空气是清新的 太美丽了 然后就待时间长了就有点寂寞了 没有动物 这些看的到 都是静态的东西呀 怎么办 别忘了是神仙呀 没
  • 设计模式学习笔记(一)之单例模式

    单例模式 作用 保证一个类只有一个实例 并且提供访问这个实例的全局访问点 应用场景有 数据库连接池 spring中 Bean默认是单例 Servlet中 每一个Servlet是单例 配置文件的类 一般是单例 优点 单例只生成一个实例 减少系
  • 小谈设计模式(1)—总序

    小谈设计模式 1 总序 专栏地址 开始操作 设计模式总论 设计模式是什么 组成要素 模式名称 问题描述 解决方案 效果描述 设计模式有什么作用 提供可重用的解决方案 提高代码的可读性和可维护性 促进代码的可扩展性 提高代码的灵活性和可重用性
  • Java设计模式:装饰者模式(Decorator Pattern)

    装饰者模式 涉及的重要设计原则 类应该对扩展开放 对修改关闭 装饰者模式定义 装饰者模式动态地将责任附加到对象上 若要扩展功能 装饰者提供了比继承更有弹性的替代方案 UML类图 装饰者模式事例 咖啡店 咖啡种类 1 深焙咖啡 DarkRoa
  • 面向过程和面向对象的语言有哪些,以及优缺点(一篇文章让你理解)

    C语言是面向过程的 而C python java是面向对象的 面向过程的编程思想将一个功能分解为一 个一个小的步骤 我们通过完成一个一 个的小的步骤来完成一个程序 优点 这种编程方式 符合我们人类的思维 编写起来相对比较简单 缺点 但是这种
  • 简单工厂模式

    简单工厂模式 一 概念 从设计模式的类型上来说 简单工厂模式是属于创建型模式 又叫做静态工厂方法 StaticFactory Method 模式 但不属于23种GOF设计模式之一 简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例 简
  • 设计模式--Abstract server模式 VS Adapter 模式

    适配器类似于现实世界里面的插头 通过适配器 我们可以将分属于不同类的两种不同类型的数据整合起来 而不必去根据某一需要增加或者修改类里面的方法 Adapter mode和Proxymode的区别 Proxy的关注点是职能转移 通过引入Prox
  • 设计模式-建造者模式

    文章目录 建造者模式 创建复杂对象的优雅方式 什么是建造者模式 建造者模式的使用场景 优缺点 示例 使用建造者模式创建电脑对象 建造者模式 创建复杂对象的优雅方式 在软件开发中 有时候我们需要创建具有复杂结构和多个组件的对象 直接在客户端代
  • Java并发编程之设计模式

    同步模式之保护性暂停 1 定义 即 Guarded Suspension 用在一个线程等待另一个线程的执行结果 要点 有一个结果需要从一个线程传递到另一个线程 让他们关联同一个 GuardedObject 如果有结果不断从一个线程到另一个线
  • 设计模式(5)-适配器模式(Adapter Pattern)

    适配器模式 Adapter Pattern 顾名思义 就像变压器 转接头差不多 就像美国的生活电压是110V 中国是220V 就需要一个变压器将220V转换成110V 或者一个Type C接口想插如USB接口的东西 你就需要一个转换器 而这
  • 每日一问:你想如何破坏单例模式?

    前言 1 单例是什么 单例模式 是一种创建型设计模式 目的是保证全局一个类只有一个实例对象 分为懒汉式和饿汉式 所谓懒汉式 类似于懒加载 需要的时候才会触发初始化实例对象 而饿汉式正好相反 项目启动 类加载的时候 就会创建初始化单例对象 1
  • 设计模式之享元模式

    享元模式 就是共享技术 对于系统中存在大量相同的对象 把他们抽取成一个对象放在缓存中进行使用 这样可以大大节省系统资源 例如 围棋棋盘上有两种棋子 一个是黑子 一个是白子 如果在下棋的时候每下一个棋子就要new一个棋子对象 那么就会有大量的
  • 组合型模式

    概述 对于这个图片肯定会非常熟悉 上图我们可以看做是一个文件系统 对于这样的结构我们称之为树形结构 在树形结构中可以通过调用某个方法来遍历整个树 当我们找到某个叶子节点后 就可以对叶子节点进行相关的操作 可以将这颗树理解成一个大的容器 容器
  • 设计模式-享元模式

    一 概念 如果在一个系统中存在多个相同的对象 那么只需要共享一份对象的拷贝 而不必为每一次使用都创建新的对象 目的是提高系统性能 上面的概念乍一听好像单例模式其实不是 单例模式只保存一个对象 但是这里可以有很多个不同对象 但是每个对象只有一
  • 设计模式(不懂)

    面试中经常问到设计模式 我才对这个东西了解了一下 才发现他是没有开发的新大陆 是oo设计的更高级别 能把设计模式搞懂 那oo你就搞的差不多了 随便看了还是很有意思的 虽然不怎么懂 百科名片 相关书籍 设计模式 Design pattern
  • 谁能想到Java多线程设计模式竟然能被图解,大佬就是大佬,太牛了

    设计模式 Design pattern 代表了最佳的实践 通常被有经验的面向对象的软件开发人员所采用 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案 这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的
  • 设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程)

    原创 疯狂的狮子Li 狮子领域 程序圈 2023 12 19 10 30 发表于辽宁 原型模式 原型模式 Prototype模式 是指 用原型实例指定创建对象的种类 并且通过拷贝这些原型 创建新的对象 原型模式是一种创建型设计模式 允许一个
  • 设计模式(三)-结构型模式(4)-组合模式

    一 为何需要组合模式 Composite 在代码设计中 有种情况是对象之间存在层次关系 即对象之间会存在父结点和子结点的关系 比如在文件管理系统中 所有文件和文件夹形成树状结构 文件夹目录里存在子文件夹和文件 文件夹属于枝结点 文件属于叶结
  • 【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则

    文章目录 一 如何判断类的职责是否足够单一 二 类的职责是否设计得越单一越好 开始学习一些经典的设计原则 其中包括 SOLID KISS YAGNI DRY LOD 等 本文主要学习单一职责原则的相关内容 单一职责原则的定义 一个类只负责完

随机推荐

  • idea读取数据库乱码,Navicat正常(解决)

    乱码问题困扰了我2天 菜的抠脚 先说说问题吧 你如果不想看这些废话就直接去下面解决 我先创建了数据库 拷贝了sql语句运行之后 Navicat正常显示 但是页面显示乱码 其实是中文latin1编码 debug跟进程序 发现在hibernat
  • msvcp140_1.dll丢失怎样修复?快速修复dll文件缺失

    msvcp140 1 dll丢失怎样修复 关于msvcp140 1 dll丢失 其实和其他dll文件的修复方法是一模一样的 你缺失了什么dll文件 那么你就在百度搜索这个dll文件 然后放到指定的文件夹就好了 解决起来还是非常的简单的 ms
  • Cocos Creator资源管理AssetManager细说一二

    关于AssetManager Asset Manager 是 Creator 在 v2 4 新推出的资源管理器 用于替代之前的 cc loader 新的 Asset Manager 资源管理模块具备加载资源 查找资源 销毁资源 缓存资源 A
  • VUE搭建项目,配置本地IP地址其他人可访问项目(整理)

    1 首先找到config文件夹目录下的 index js文件 Various Dev Server settings host localhost 将localhost进行替换成 0 0 0 0 host 0 0 0 0 can be ov
  • 如何使用USB接口对C51单片机下载固件

    使用USB转UART芯片对单片机下载固件时会遇到的问题 C51系列单片机在下载固件的时候需要断电重启 在使用RS232接口的时候不会遇到什么困难 因为RS232不需要进行识别 但是现在使用USB转UART的芯片时会遇到问题 因为USB设备在
  • CIFAR-10训练模型(ResNet18)

    1 搭建环境 环境在实验进行时已经搭建完毕 具体步骤就不过多赘述 参考 https blog csdn net weixin 39574469 article details 117454061 接下来只需导入所需的包即可 import n
  • python中列表数据汇总和平均值_如何从记录列表中计算平均值

    所以我正在做一个作业 当从一个数据列表中计算一个平均值 数据是从一个外部的 txt文件中读取的 时 我似乎遇到了麻烦 具体来说 我要做的是从下面的数据列表中读取数据记录 在1 2 2014 Frankton 42305 67 23 12 4
  • 高德地图API INVALID_USER_SCODE问题以及keystore问题

    转载地址 http m blog csdn net article details id 50448014 请尊重原创 今天这篇文章会给大家介绍三个问题 1 接入API时出现invalid user scode问题 首先进行第一个大问题 接
  • python连接数据库设置编码_python连接mysql数据库——编码问题

    编码问题 1 连接数据库语句 在利用pycharm连接本地的mysql数据库时 要考虑到的是将数据库语句填写完整 困扰了一下午的问题就是连接语句并没有加入编码设置 db pymysql connect host localhost user
  • 如何利用计算机打印较大的字,如何在一张A4纸上打印一个超大字?

    是不是很想打印超大字 要是硬件上去了 就什么话也不用说了 可惜的是 手中只有一个A4的打印机 怎么办 还是有办法的 用Microsoft Office 2003就可以 我由于学校工作的原因 打印机只能打A4的纸 有时又想打超大字 不得不用现
  • TensorFlow零基础入门,实现手写数字识别

    TensorFlow 是一个用于人工智能的开源神器 主要为深度学习算法提供了很多函数 以及独特的运算支持 废话不多说直接上干货 我的环境 python3 7 tensorflow 1 13 2 numpy 1 20 2 1 入门示例 imp
  • 算法分享三个方面学习方法(做题经验,代码编写经验,比赛经验)

    目录 0 前言 遇到OI不要慌 只要道路对了 就不怕遥远 1 做题经验谈 1 1 做题的目的 1 2 我对于算法比赛的题目的看法 1 2 1 类似题 1 2 2 套模型 1 3 在训练过程中如何做题 1 4 一些建议 提高算法能力 1 5
  • AJAX分页以及IFRAME载入

    AJAX获取数据并分页显示 ul class movList ul div div
  • leetcode-03. 数组中重复的数字刷题笔记(c++)

    写在前面 难度 简单 unordered map 或 sort排序 大数组方法异常溢出 数据量 小数据量 数组元素作为下标 大数据量 无需map映射 耗费空间 sort排序 前后元素是否等值 题目详情 找出数组中重复的数字 在一个长度为 n
  • 一条慢SQL引发的改造

    前言 闲鱼服务端在做数据库查询时 对每一条SQL都需要仔细优化 尽可能使延时更低 带给用户更好的体验 但是在生产中偶尔会有一些情况怎么优化都无法满足业务场景 本文通过对一条慢SQL的真实改造 介绍解决复杂查询的一种思路 以及如何使得一条平均
  • Seata 处理分布式事务

    文章目录 1 Seata 简介2 2 Seata的安装 2 1 修改配置文件 2 2 在nacos上创建配置文件 seataServer yaml 2 3 安装路径seata seata server 1 6 0 seata script
  • C# —— 面向对象编程练习

    C 面向对象编程练习 基础题 代码如下 class Program static void Main string args rectangle r new rectangle Console WriteLine 调用不含参构造函数初始化后
  • [ElasticSearch]painless脚本获取字段方式及性能比较

    一 3种常见方式 doc fieldname 常用于在搜索或排序等无状态操作时进行使用 params source fieldname 也是常用在搜索或排序中 但更多用在获取额外字段时灵活操作时使用 如不获取某个字段直接使用params s
  • 基于图像形态学处理和边缘提取算法的路面裂痕检测matlab仿真

    目录 1 算法运行效果图预览 2 算法运行软件版本 3 部分核心程序 4 算法理论概述 5 算法完整程序工程 1 算法运行效果图预览 2 算法运行软件版本 matlab2022a 3 部分核心程序 Rr Cc size Image1 获取
  • 适配器模式(Adapter)

    适配器是一种结构型设计模式 它能使接口不兼容的对象能够相互合作 又称封装器模式 Wrapper 适配器模式通过封装对象将复杂的转换过程隐藏于幕后 被封装的对象甚至察觉不到适配器的存在 例如 你可以使用一个将所有数据转换为英制单位 如英尺和英