【设计模式】如何用C++实现适配器模式

【设计模式】如何用C++实现适配器模式

一、问题背景

用到过很多次适配器模式,一直不理解为什么用这种模式,好像这个模式天生就该如此使用。

实际上,我们很多的理念都源于一些简朴的思想,这些思想不一定高深,但是在保证代码质量,实现高内聚低耦合的设计思想上也许有所益处。

解决问题时,本着好读书不求甚解的思想去做,以求采用有效合适的方式合理解决问题,这没有错。

但是前置学习或者事后复盘时,也许应该知其然知其所以然。学习其思想,解构其体系,重构其内涵,让这种简朴的思想经历复杂的过程后,再次回归简朴。

二、什么是适配器模式?

适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。简单来说,就是将一个类的接口转换成客户希望的另一个接口,使得原本不兼容的类可以一起工作。

适配器模式的核心思想就是通过引入一个中间层(适配器类),来解决不同接口之间的不兼容问题,使得它们能够协同工作。这种模式在软件开发中非常常见,尤其是在需要集成不同系统或框架的时候。

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

  1. 类适配器模式: 适配器类继承了原有类和目标接口,通过继承来实现接口转换。
  2. 对象适配器模式: 适配器类持有原有类的实例,通过组合的方式来实现接口转换。

一般情况下使用对象适配器,因为它耦合度更低,更符合面向对象的设计原则。

三、为什么使用适配器模式?

  1. 解决接口不兼容问题: 当你希望使用某个类,但是其接口与其他代码不兼容时,可以使用适配器类。

  2. 兼容旧系统: 适配器模式允许你创建一个中间层类,其可作为代码与遗留类、第三方类或提供怪异接口的类之间的转换器。

  3. 提高代码复用性: 通过适配器,可以将已有的类适配成新的接口,从而在新的系统中重用这些类。

四、实现步骤

有现存接口类AClassBClass,可以定义Adapter去适配这些旧接口以实现新功能,在main函数中调用适配器接口来实现自身功能,而无需关注AClassBClass的具体实现。

假设旧接口AFunc1、AFunc2、BFunc1、BFunc2不能满足用户需求,用户需要的是AFunc和BFunc任意组合后的接口,如果将这个接口放在用户调用层去实现的话,用户需要同时持有和管理A对象和B对象,在对象较少的情况下采用这种方式或许可行。

随着需要管理的对象越来越多,用户实现与底层之间的耦合将会越来越深。牵一发而动全身,任何一个底层的修改都可能导致原先的功能出现异常。

而采用适配器模式,我们将接口对象使用适配器管理,针对用户业务场景划分适配器的类别,仅需少许适配器,就可实现用户需要的所有功能。此时所有适配器都是对应用户场景,方便用户理解并且无需关心底层原始实现。

1. 旧接口类someClass

./someClass/AClass.h

#ifndef SOMECLASS_ACLASS_H
#define SOMECLASS_ACLASS_H
namespace SomeClass
{class AClass {public:AClass();~AClass();void AFunc1();void AFunc2();};
}
#endif

./someClass/AClass.cpp

#include "AClass.h"
#include <iostream>using namespace SomeClass;AClass::AClass()
{std::cout << "In AClass, construction" << std::endl;
}AClass::~AClass()
{std::cout << "In AClass, destruction" << std::endl;
}void AClass::AFunc1()
{std::cout << "In AClass, Func1" << std::endl;
}void AClass::AFunc2()
{std::cout << "In AClass, Func2" << std::endl;
}

./someClass/BClass.h

#ifndef SOMECLASS_BCLASS_H
#define SOMECLASS_BCLASS_H
namespace SomeClass
{class BClass {public:BClass();~BClass();void BFunc1();void BFunc2();};
}
#endif

./someClass/BClass.cpp

#include "BClass.h"
#include <iostream>using namespace SomeClass;BClass::BClass()
{std::cout << "In BClass, construction" << std::endl;
}BClass::~BClass()
{std::cout << "In BClass, destruction" << std::endl;
}void BClass::BFunc1()
{std::cout << "In BClass, Func1" << std::endl;
}void BClass::BFunc2()
{std::cout << "In BClass, Func2" << std::endl;
}

2. 对象适配器

./adapter/Adapter.h

#ifndef ADAPTER_H
#define ADAPTER_H
namespace Adapter
{class Adapter {public:virtual void FUNCA1B1() = 0;virtual void FUNCA1B2() = 0;virtual void FUNCA2B1() = 0;virtual void FUNCA2B2() = 0;};
}
#endif

./adapter/AdapterImpl.h

#ifndef ADAPTER_IMPL_H
#define ADAPTER_IMPL_H
#include "Adapter.h"
#include "AClass.h"
#include "BClass.h"
namespace Adapter
{class AdapterImpl : public Adapter {public:AdapterImpl();~AdapterImpl();void FUNCA1B1() override;void FUNCA1B2() override;void FUNCA2B1() override;void FUNCA2B2() override;private:SomeClass::AClass m_aClass;SomeClass::BClass m_bClass;};
}
#endif

./adapter/AdapterImpl.cpp

#include "AdapterImpl.h"
#include <iostream>
using namespace Adapter;
using namespace SomeClass;AdapterImpl::AdapterImpl()
{std::cout << "In AdapterImpl, construction" << std::endl;
}AdapterImpl::~AdapterImpl()
{std::cout << "In AdapterImpl, destruction" << std::endl;
}void AdapterImpl::FUNCA1B1()
{m_aClass.AFunc1();m_bClass.BFunc1();
}void AdapterImpl::FUNCA1B2()
{m_aClass.AFunc1();m_bClass.BFunc2();
}void AdapterImpl::FUNCA2B1()
{m_aClass.AFunc2();m_bClass.BFunc1();
}void AdapterImpl::FUNCA2B2()
{m_aClass.AFunc2();m_bClass.BFunc2();
}

3. main函数调用

./main.cpp

#include "Adapter.h"
#include "AdapterImpl.h"
#include <memory>int main()
{std::shared_ptr<Adapter::Adapter> adapter = std::make_shared<Adapter::AdapterImpl>();adapter->FUNCA1B1();adapter->FUNCA1B2();adapter->FUNCA2B1();adapter->FUNCA2B2();return 0;
}

4. 编写CMakeLists.txt

# 设置项目名称和最低CMake版本
cmake_minimum_required(VERSION 3.10)
set(ProjectName Adapter)
project(${ProjectName})set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)include_directories(.adaptersomeClass
)file(GLOB LIB_FILE adapter/*someClass/*)add_executable(${ProjectName}main.cpp${LIB_FILE})

此时文件树结构如下:

在这里插入图片描述

5. 编译运行

mkdir build
cd build
cmake ..
make -j12
./Adapter

运行结果如下

In AClass, construction
In BClass, construction
In AdapterImpl, construction
In AClass, Func1
In BClass, Func1
In AClass, Func1
In BClass, Func2
In AClass, Func2
In BClass, Func1
In AClass, Func2
In BClass, Func2
In AdapterImpl, destruction
In BClass, destruction
In AClass, destruction

main函数不感知AClass具体实现的情况下使用Adapter对象适配器,同时持有AClassBClass的实例,实现了组合AClass接口和BClass接口的功能。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解八大排序(一)------(插入排序,选择排序,冒泡排序,希尔排序)

文章目录 前言1.插入排序&#xff08;InsertSort&#xff09;1.1 核心思路1.2 实现代码 2.选择排序&#xff08;SelectSort&#xff09;2.1 核心思路2.2 实现代码 3.冒泡排序&#xff08;BubbleSort&#xff09;3.1 核心思路3.2 实现代码 4.希尔排序&#xff08;ShellSort&…

《Django 5 By Example》阅读笔记:p679-p765

《Django 5 By Example》学习第10天&#xff0c;p679-p765总结&#xff0c;总计87页。 一、技术总结 1.channel 书里通过聊天软件功能演示Django中channel以及异步编程的应用&#xff0c;本人对这块不是很熟悉&#xff0c;不做评价。 2.deployment(部署) services:db:imag…

kali搭建pikachu靶场

前言&#xff1a; 总所周知搭个网站需要有apachemysqlphp&#xff0c;Apache是一个开源的Web服务器软件&#xff0c; MySQL是一种关系型数据库管理系统&#xff08;数据库&#xff09;&#xff0c;PHP是一种在服务器上执行的脚本语言 文章内容来自&#xff1a;【黑帽编程与攻…

C++时间复杂度与空间复杂度

一、时间复杂度&#xff08;Time Complexity&#xff09; 1. 概念 时间复杂度是用来衡量算法运行时间随着输入规模增长而增长的量级。它主要关注的是算法执行基本操作的次数与输入规模之间的关系&#xff0c;而非具体的运行时间&#xff08;因为实际运行时间会受硬件、编程语…

【软考】系统架构设计师-信息安全技术基础

信息安全核心知识点 信息安全5要素&#xff1a;机密性、完整性、可用性、可控性、审查性 信息安全范围&#xff1a;设备安全、数据安全、内容安全、行为安全 网络安全 网络安全的隐患体现在&#xff1a;物理安全性、软件安全漏洞、不兼容使用安全漏洞、选择合适的安全哲理 …

SQL Server Management Studio 的JDBC驱动程序和IDEA 连接

一、数据库准备 &#xff08;一&#xff09;启用 TCP/IP 协议 操作入口 首先&#xff0c;我们要找到 SQL Server 配置管理器&#xff0c;操作路径为&#xff1a;通过 “此电脑” 右键选择 “管理”&#xff0c;在弹出的 “计算机管理” 窗口中&#xff0c;找到 “服务和应用程…

类和对象——static 成员,匿名对象(C++)

1.static成员 a&#xff09;⽤static修饰的成员变量&#xff0c;称之为静态成员变量&#xff0c;静态成员变量⼀定要在类外进行初始化。 b&#xff09;静态成员变量为所有类对象所共享&#xff0c;不属于某个具体的对象&#xff0c;不存在对象中&#xff0c;存放在静态区。 …

游戏引擎学习第17天

视频参考:https://www.bilibili.com/video/BV1LPUpYJEXE/ 回顾上一天的内容 1. 整体目标&#xff1a; 处理键盘输入&#xff1a;将键盘输入的处理逻辑从平台特定的代码中分离出来&#xff0c;放入更独立的函数中以便管理。优化消息循环&#xff1a;确保消息循环能够有效处理 …

【JavaEE初阶 — 多线程】线程池

目录 1. 线程池的原理 1.1 为什么要有线程池 1.2 线程池的构造方法 1.3 线程池的核心参数 1.4 TimeUnit 1.5 工作队列的类型 1.6 工厂设计模式 1.6.1 工厂模式概念 1.6.2 使用工厂模式的好处 1.6.3 使用工厂模式的典型案例 1.6.4 Thread…

基于Vue+SpringBoot的求职招聘平台

平台概述 本平台是一个高效、便捷的人才与职位匹配系统&#xff0c;旨在为求职者与招聘者提供一站式服务。平台内设三大核心角色&#xff1a;求职者、招聘者以及超级管理员&#xff0c;每个角色拥有独特的功能模块&#xff0c;确保用户能够轻松完成从信息获取到最终录用的整个…

黑马点评 秒杀下单出现的问题:服务器异常---java.lang.NullPointerException: null(已解决)

前言&#xff1a; 在此之前找了好多资料&#xff0c;查了很多&#xff0c;都没有找到对应解决的方法&#xff0c;虽然知道是userid为空&#xff0c;但不知道要修改哪里&#xff0c;还是自己的debug能力不足&#xff0c;以后得多加练习。。。 问题如下&#xff1a; 点击限时抢…

使用GDB或Delve对已经运行起来的Go程序进行远程调试

同步发布在我的博客&#xff0c;欢迎来点赞。 使用 GDB 或 Delve 对已经运行起来的 Go 程序进行远程调试 使用 GDB 或 Delve 对已经运行起来的 Go 程序进行远程调试 背景 Java 程序可以很方便地通过 jdwp 参数指定一个对外端口进行远程调试&#xff0c;如 java \ -agentlib…

如何解决pdf.js跨域从url动态加载pdf文档

摘要 当我们想用PDF.js从URL加载文档时&#xff0c;将会因遇到跨域问题而中断&#xff0c;且是因为会触发了PDF.js和浏览器的双重CORS block&#xff0c;这篇文章将会介绍&#xff1a;①如何禁用pdf.js的跨域&#xff1f;②如何绕过浏览器的CORS加载URL文件&#xff1f;②如何使…

Jmeter中的断言(三)

9--MD5Hex断言 功能特点 数据完整性验证&#xff1a;验证响应数据的 MD5 哈希值是否符合预期。简单配置&#xff1a;只需提供预期的 MD5 哈希值即可。灵活配置&#xff1a;可以设置多个断言条件&#xff0c;满足复杂的测试需求。 配置步骤 添加 MD5Hex 断言 右键点击需要添加…

Tomcat和Nginx原理说明

Tomcat Tomcat 是一个开源的 Java 应用服务器&#xff0c;它由多个关键组件组成。这些组件共同协作&#xff0c;实现了 Servlet 容器的功能。以下是 Tomcat 的核心组件说明及其逻辑架构的示意图。 1. Tomcat 核心组件说明 (1) Server 描述&#xff1a;Tomcat 的顶级组件&…

vmWare虚拟环境centos7安装Hadoop 伪分布式实践

背景&#xff1a;近期在研发大数据中台&#xff0c;需要研究Hadoop hive 的各种特性&#xff0c;需要搭建一个Hadoop的虚拟环境&#xff0c;本来想着使用dock &#xff0c;但突然发现docker 公共仓库的镜像 被XX 了&#xff0c;无奈重新使用vm 搭建虚拟机。 大概经历了6个小时完…

10 基于深度学习的目标检测

首次完成时间&#xff1a;2024 年 11月 20 日 1. 使用OpenCV的dnn模块实现图像分类。 1&#xff09;程序代码&#xff1a; import numpy as np import cv2# 解析标签文件 row open("model1/synset_words.txt").read().strip().split("\n") class_label …

ssl证书,以 Nginx 为例

文章目录 1 证书概述1.1 常见证书格式1.2 证书的几种扩展名1.3 关于 PKCS#12 格式 2 Nginx 下证书配置2.1 证书的工作原理2.1.1 单向认证2.1.2 双向认证 2.2 CA 机构签发2.2.1 免费 SSL 证书申请2.2.2 双向认证 2.3 自签证书2.3.1 单向认证2.3.2 双向认证 附录 1&#xff1a;Wi…

android:taskAffinity 对Activity退出时跳转的影响

android:taskAffinity 对Activity跳转的影响 概述taskAffinity 的工作机制taskAffinity对 Activity 跳转的影响一个实际的开发问题总结参考 概述 在 Android 开发中&#xff0c;任务栈&#xff08;Task&#xff09;是一个核心概念。它决定了应用程序的 Activity 如何相互交互以…

专家PID控制

专家PID控制&#xff08;Expert PID Control&#xff09;是一种结合了传统PID控制和专家系统思想的控制方法。它通过引入专家经验、规则和推理机制&#xff0c;以改善PID控制器在面对复杂系统时的性能。专家PID控制不仅仅依赖于固定的PID参数&#xff08;比例、积分、微分&…