设计模式17-适配模式

news2024/9/21 22:12:35

设计模式17-适配模式

  • 动机
  • 定义与结构
  • C++代码推导
  • 总结
  • 应用
  • 具体应用示例

动机

  • 在软件系统中由于应用环境的变化常常需要将一些现存的对象。放到新的环境中去应用。但是新环境要求的接口是这些现存对象所不满足的。
  • 那么这种情况下如何应对这种迁移的变化?如何既能利用现有对象的良好实现用了满足新的应用环境所要求的接口。

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

  • 当你想使用一个已经存在的类,但它的接口不符合你的需求时。
  • 当你想创建一个可以重用的类,该类可以和不兼容的接口协同工作时。
  • 当你想使用一些现有的子类,但是不能进行子类化来匹配接口时,因为这会导致每个子类需要调整。

定义与结构

定义

  • 适配器模式将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

在这里插入图片描述

适配器模式包含以下主要角色:

  1. 目标(Target)接口:定义客户所需的接口。
  2. 适配者(Adaptee)类:定义一个已经存在的接口,这个接口需要适配。
  3. 适配器(Adapter)类:将适配者接口转换成目标接口,使得客户端可以通过目标接口与适配者进行通信。

适配器模式有两种实现方式:

  • 类适配器(Class Adapter):使用多重继承实现适配。
  • 对象适配器(Object Adapter):使用组合实现适配。

类适配器模式UML图:

----------------      ----------------      -----------------
|      Target     |<---|   Adapter    |<|  Adaptee         |
|----------------|   |----------------|   |---------------|
|+request()      |   |+request()      |   |+specificRequest|
-----------------      -----------------      ---------------

对象适配器模式UML图:

----------------      ----------------      ----------------
|      Target     |<---|   Adapter    |<| Adaptee          |
|----------------|   |----------------|   |----------------|
|+request()      |   |+request()      |   |+specificRequest|
-----------------   | -adaptee: Adaptee|   -----------------
                   -------------------

C++代码推导

类适配器模式:

#include <iostream>

// 目标接口
class Target {
public:
    virtual void request() = 0;
    virtual ~Target() = default;
};

// 适配者类
class Adaptee {
public:
    void specificRequest() {
        std::cout << "Adaptee specific request." << std::endl;
    }
};

// 类适配器
class Adapter : public Target, private Adaptee {
public:
    void request() override {
        specificRequest();
    }
};

// 客户端代码
int main() {
    Target* target = new Adapter();
    target->request(); // 调用适配器的方法
    delete target;
    return 0;
}

对象适配器模式:

#include <iostream>

// 目标接口
class Target {
public:
    virtual void request() = 0;
    virtual ~Target() = default;
};

// 适配者类
class Adaptee {
public:
    void specificRequest() {
        std::cout << "Adaptee specific request." << std::endl;
    }
};

// 对象适配器
class Adapter : public Target {
private:
    Adaptee* adaptee;

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

    void request() override {
        adaptee->specificRequest();
    }
};

// 客户端代码
int main() {
    Adaptee* adaptee = new Adaptee();
    Target* target = new Adapter(adaptee);
    target->request(); // 调用适配器的方法
    delete target;
    delete adaptee;
    return 0;
}

总结

  • 适配器模式主要应用于希望服用一些现存的类可用于复用环境要求不一致的情况。在遗留代码复用嗯类库迁移等方面非常常用。

  • 世界模式书中定义了两种适配器模式的实现结构:对象适配器和类适配器。但是类适配器采用多继承的实现方式。一般不推荐使用。对象适配器采用对象组合的方式更符合松耦合的设计理念。

  • 适配器模式可以实现的非常灵活不必拘泥于设计模式书中定义的两种结构。例如完全可以将适配器模式中的现存对象作为新的接口方法参数,来达到适配的目的。

优点:

  1. 单一职责原则:可以将接口或数据转换代码从原始类中分离出来。
  2. 开闭原则:无需修改客户端代码即可引入新的适配器类。
  3. 灵活性高:类适配器使用多重继承,可以适配多个适配者类。对象适配器使用组合,可以在运行时对适配者进行替换。

缺点:

  1. 类适配器的缺点
    • 不支持适配多个适配者类,因为C++不支持多继承多个实现类。
    • 破坏了Adaptee类和Adapter类的独立性,因为适配器继承自适配者。
  2. 对象适配器的缺点
    • 在某些情况下,可能需要多次调用适配者的方法,导致性能开销较大。

应用

适配器模式在实际应用中非常广泛,常见的应用场景包括:

  1. 使用遗留代码库:当你想使用一个现有的类,但它的接口不符合你的需求时,可以使用适配器模式进行适配。
  2. 与第三方库集成:当你想使用第三方库,但它的接口与你的代码不兼容时,可以使用适配器模式进行适配。
  3. 处理多种接口:当你需要创建一个可以与多个不兼容接口进行交互的类时,可以使用适配器模式进行适配。

具体应用示例

以下是一个适配器模式的具体应用示例:将一个旧的日志系统适配到新的日志系统中。

旧日志系统:

#include <iostream>

class OldLogger {
public:
    void oldLog(const std::string& message) {
        std::cout << "OldLogger: " << message << std::endl;
    }
};

新日志系统接口:

class NewLogger {
public:
    virtual void log(const std::string& message) = 0;
    virtual ~NewLogger() = default;
};

适配器类:

class LoggerAdapter : public NewLogger {
private:
    OldLogger* oldLogger;

public:
    LoggerAdapter(OldLogger* logger) : oldLogger(logger) {}

    void log(const std::string& message) override {
        oldLogger->oldLog(message);
    }
};

客户端代码:

int main() {
    OldLogger* oldLogger = new OldLogger();
    NewLogger* logger = new LoggerAdapter(oldLogger);

    logger->log("This is a log message."); // 使用适配器进行日志记录

    delete logger;
    delete oldLogger;
    return 0;
}

通过上述示例,我们将旧的日志系统适配到新的日志系统接口中,使得客户端代码可以无缝使用旧的日志系统,同时保持代码的灵活性和可维护性。这就是适配器模式在实际开发中的典型应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1964926.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《零散知识点 · SpringBoot 整合邮件功能》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

光伏气象仿真系统主要功能点

光伏电站的效能与经济效益受气象条件、地理位置、设计布局等多种因素影响显著。因此&#xff0c;光伏气象仿真系统应运而生&#xff0c;成为提升光伏项目规划、设计、运营管理水平的重要工具。该系统集成了气象数据分析、发电量分析、投融资分析、损耗估算分析及光伏设计等多项…

重磅惊喜!OpenAI突然上线GPT-4o超长输出模型!「Her」高级语音模式已开放测试

在最近的大模型战争中&#xff0c;OpenAI似乎很难维持霸主地位。虽然没有具体的数据统计&#xff0c;但Claude3.5出现后&#xff0c;只是看网友们的评论&#xff0c;就能感觉到OpenAI订阅用户的流失&#xff1a; Claude3.5比GPT-4o好用&#xff0c;为什么我们不去订阅Claude呢&…

【TS】基础(一)

TS基础学习 TS(TypeScript)TS的特性TS的优缺点优点缺点 TS和JS的区别TS常用类型原始类型数组类型别名 type联合类型 |函数对象接口 interface继承 extendsinterface和type的区别 类型断言枚举enum任意类型any TS(TypeScript) TS是JS类型的超集&#xff08;在JavaScript的基础上…

为什么说脱离决策的数据分析都是无用功

如果你问我数据分析师最重要的能力是什么&#xff0c;我的回答是数据驱动决策&#xff0c;这是数据分析师最值钱的能力&#xff0c;没有之一。 因为数据的价值在于挖掘&#xff0c;与业务和市场进行关联&#xff0c;找到机会点。抛开这个&#xff0c;数据本身一文不值&#xf…

C语言笔记(第n版):知识清单

注&#xff1a;本文参考自【C reference - cppreference.com】和【C 语言参考 | Microsoft Learn】&#xff0c;颇有点借花献佛的意味…… C 程序是一系列包含声明的文本文件&#xff08;通常为头文件和源文件&#xff09;的序列。它们经过转换成为可执行程序&#xff0c;当操作…

Bugku ---Web---全题目解析 超详细步骤 持续更新

目录 1.Simple_SSTI_1 2.Simple_SSTI_2 3.滑稽 4.计算器 5.矛盾 6.GET ————————————————————分割线———————————————————— 1.Simple_SSTI_1 SSTI是服务器端模板注入 F12查看下源码 在 Flask 中&#xff0c;双大括号 {{ }} 用…

ElasticSearch父子索引实战

关于父子索引 ES底层是Lucene,由于Lucene实际上是不支持嵌套类型的,所有文档都是以扁平的结构存储在Lucene中,ES对父子文档的支持,实际上也是采取了一种投机取巧的方式实现的. 父子文档均以独立的文档存入,然后添加关联关系,且父子文档必须在同一分片,由于父子类型文档并没有…

Oracle LiveLabs实验:Improve application performance with True Cache

此实验申请地址在这里&#xff0c;时间为120分钟。 帮助见这里。 简介 参加本次动手实践研讨会&#xff0c;了解如何使用 True Cache 通过卸载查询和减少对主数据库的请求和连接数量来提高可扩展性。本次研讨会基于连接到配置了 True Cache 的主数据库的计算实例&#xff08…

从零手写实现 mybatis 系列(零)mybatis 核心特性

拓展阅读 第一节 从零开始手写 mybatis&#xff08;一&#xff09;MVP 版本 中我们实现了一个最基本的可以运行的 mybatis。 第二节 从零开始手写 mybatis&#xff08;二&#xff09;mybatis interceptor 插件机制详解 第三节 从零开始手写 mybatis&#xff08;三&#xff0…

最低工资标准数据(2001-2023年不等)、省市县,整理好的面板数据(excel格式)

时间范围&#xff1a;2001-2022年 具体内容&#xff1a;一&#xff1a;最低工资数据标准时间&#xff1a;2012-2021包含指标&#xff1a; 省份城市/区县小时最低工资标准&#xff08;非全日制&#xff09;月最低工资标准实施日期 样例数据&#xff1a; 二&#xff1a;各省最低…

Spring Cloud Gateway网关的高级特性之GatewayFilter Factories(路由过滤器)

1、GatewayFilter Factories&#xff08;路由过滤器&#xff09; 官方访问地址&#xff1a;点击这里 来自官方的解释如下图所示&#xff1a; 简单来说就是&#xff1a; 客户端向 Spring Cloud Gateway 发送请求。如果请求与某个路由匹配&#xff0c;则该请求会被传递给 Ga…

OpenEuler安装部署教程

目录 OpenEuler安装部署教程 MobaXterm一款全能的远程工具 yum安装软件 vim编辑器&#xff08;了解&#xff09; 防火墙 常用命令 网络工具netstat & telnet 进程管理工具top ps 磁盘free、fdisk 用户、组&#xff08;了解&#xff09; 权限&#xff08;了解&am…

实验3-9 三天打鱼两天晒网

//实验3-9 三天打鱼两天晒网/* 中国有句俗语叫“三天打鱼两天晒网”。 假设某人从某天起&#xff0c;开始“三天打鱼两天晒网”&#xff0c; 问这个人在以后的第N天中是“打鱼”还是“晒网”&#xff1f; */#include<stdio.h> #include<math.h> int main(){int n; …

Photoshop的下载和安装教程

找到Adobe 的官网 https://www.adobe.com/cn/ 创建一个新的账户,如果你没有账户的话 后面安装步骤来注册 下载和安装 登录之后 点击 点击 点击 然后进行下载和安装 然后就是漫长的等待 安装完成 点击 这只是一个安装Photoshop的教程,也可以根据别人的安装包来进行安装

ThreadPoolExecutor工作原理及源码详解

一、前言 创建一个线程可以通过继承Thread类或实现Runnable接口来实现&#xff0c;这两种方式创建的线程在运行结束后会被虚拟机回收并销毁。若线程数量过多&#xff0c;频繁的创建和销毁线程会浪费资源&#xff0c;降低效率。而线程池的引入就很好解决了上述问题&#xff0c;…

万字详解 MapStruct Plus,带你快速上手!

与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 前言一、为什么要用 MapStruct&#xff08;背景&#xff09;二、MapStruct Plus 的快速开始1. 引入依赖2. 指定对象映射关系3. 编写测试代码4. 运行结果5. 原理解析 三、自定义实体类中的属性转换1. 自定义一个类型…

【IO】使用父子进程完成两个文件的拷贝,父进程拷贝前一半内容,子进程拷贝后一半内容,子进程结束后退出,父进程回收子进程的资源

1、使用父子进程完成两个文件的拷贝&#xff0c;父进程拷贝前一半内容&#xff0c;子进程拷贝后一半内容&#xff0c;子进程结束后退出&#xff0c;父进程回收子进程的资源 #include <myhead.h>int main(int argc, const char *argv[]) {//判断输入的格式是否符合要求i…

预测性维护:一种基于因果技术语言处理 (CTLP) 的智能故障诊断方法

关键词&#xff1a;预测性维护、因果贝叶斯网络、ROX数据 在工业运营和维护领域&#xff0c;资产绩效最大化和风险最小化至关重要。随着工业设备组件的磨损和恶化&#xff0c;系统会表现出一系列变化&#xff0c;这些变化的严重程度会逐渐增加&#xff0c;直到最终发生故障。在…