C++适配器模式之可插入适配器的实现模式和方法
可插入适配器与Adaptee的窄接口
在C++适配器模式中,可插入适配器(Pluggable Adapter)是指适配器类的设计允许在运行时动态地插入不同的Adaptee对象,从而使适配器具有灵活性和可扩展性。这种设计使得适配器不仅限于适配一个特定的Adaptee,而是可以适配多个不同的Adaptee。
Adaptee的窄接口(Narrow Interface)是指Adaptee提供的接口只包含有限的方法,这些方法可能不足以满足客户端的需求。适配器的工作就是将这些有限的方法适配成客户端所期望的更丰富的接口。
可插入适配器的实现方式
可插入适配器有以下几种实现方式:
- 对象适配器模式(Object Adapter):适配器持有Adaptee实例的引用。
- 类适配器模式(Class Adapter):适配器通过多重继承同时继承目标接口和Adaptee。
- 双适配器模式(Two-Way Adapter):适配器可以同时作为Adaptee和Target接口的适配器。
下面分别介绍这三种实现方式,并给出UML图和C++代码示例。
1. 对象适配器模式(Object Adapter)
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|| - adaptee: Adaptee||-------------------|| + request() ||-------------------|| + doSomething() |+-------------------+||V+-------------------+| Adaptee ||-------------------|| + doSomething() |+-------------------+
C++ 代码示例
#include <iostream>// Target 接口
class Target {
public:virtual void request() const = 0;
};// Adaptee 类
class Adaptee {
public:void doSomething() const {std::cout << "Adaptee: Doing something." << std::endl;}
};// Adapter 类
class Adapter : public Target {
private:Adaptee* adaptee;public:Adapter(Adaptee* adaptee) : adaptee(adaptee) {}void request() const override {adaptee->doSomething();}
};// 客户端代码
int main() {Adaptee* adaptee = new Adaptee();Target* adapter = new Adapter(adaptee);adapter->request(); // 调用适配器的方法delete adaptee;delete adapter;return 0;
}
2. 类适配器模式(Class Adapter)
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|| + doSomething() ||-------------------|+-------------------+^||+-------------------+| Adaptee ||-------------------|| + doSomething() |+-------------------+
C++ 代码示例
#include <iostream>// Target 接口
class Target {
public:virtual void request() const = 0;
};// Adaptee 类
class Adaptee {
public:void doSomething() const {std::cout << "Adaptee: Doing something." << std::endl;}
};// Adapter 类
class Adapter : public Target, private Adaptee {
public:void request() const override {doSomething();}
};// 客户端代码
int main() {Target* adapter = new Adapter();adapter->request(); // 调用适配器的方法delete adapter;return 0;
}
3. 双适配器模式(Two-Way Adapter)
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|| + doSomething() ||-------------------|+-------------------+^||+-------------------+| Adaptee ||-------------------|| + doSomething() |+-------------------+
C++ 代码示例
#include <iostream>// Target 接口
class Target {
public:virtual void request() const = 0;
};// Adaptee 接口
class Adaptee {
public:virtual void doSomething() const = 0;
};// 具体的 Adaptee 类
class ConcreteAdaptee : public Adaptee {
public:void doSomething() const override {std::cout << "ConcreteAdaptee: Doing something." << std::endl;}
};// Adapter 类
class Adapter : public Target, public Adaptee {
private:ConcreteAdaptee* adaptee;public:Adapter(ConcreteAdaptee* adaptee) : adaptee(adaptee) {}void request() const override {adaptee->doSomething();}void doSomething() const override {adaptee->doSomething();}
};// 客户端代码
int main() {ConcreteAdaptee* adaptee = new ConcreteAdaptee();Adapter* adapter = new Adapter(adaptee);adapter->request(); // 调用适配器的方法adapter->doSomething(); // 调用 Adaptee 的方法delete adaptee;delete adapter;return 0;
}
总结
可插入适配器和Adaptee的窄接口是适配器模式的两个重要概念。通过对象适配器、类适配器和双适配器这三种实现方式,适配器可以灵活地适配不同的Adaptee对象,解决接口不兼容的问题。这些实现方式各有优缺点,选择哪种方式取决于具体的需求和设计考虑。
在窄接口实现中,可以通过抽象实现、使用代理对象和参数化适配器方式来实现适配器模式。下面分别说明这三种方式,并给出相应的UML图和C++代码示例。
1. 抽象实现(Abstract Implementation)
抽象实现通过定义一个抽象基类来提供Adaptee的窄接口,然后具体的Adaptee实现类从这个抽象基类继承。适配器类可以持有这个抽象基类的引用,从而在运行时适配不同的具体实现。
UML 类图
+-------------------+ +-------------------+ +-------------------+
| Target | | Adapter | | Adaptee1 |
|-------------------| |-------------------| |-------------------|
| + request() |<----------|>+ request() |<----------|>+ doSomething() |
+-------------------+ |-------------------| +-------------------+| - adaptee: Adaptee||-------------------|+-------------------+^||+-------------------+ +-------------------+| AbstractAdaptee | | Adaptee2 ||-------------------| |-------------------|| + doSomething() |<----------|>+ doSomething() |+-------------------+ +-------------------+
C++ 代码示例
#include <iostream>// Target 接口
class Target {
public:virtual void request() const = 0;
};// 抽象的 Adaptee 基类
class AbstractAdaptee {
public:virtual void doSomething() const = 0;
};// 具体的 Adaptee 实现类 1
class Adaptee1 : public AbstractAdaptee {
public:void doSomething() const override {std::cout << "Adaptee1: Doing something." << std::endl;}
};// 具体的 Adaptee 实现类 2
class Adaptee2 : public AbstractAdaptee {
public:void doSomething() const override {std::cout << "Adaptee2: Doing something." << std::endl;}
};// Adapter 类
class Adapter : public Target {
private:AbstractAdaptee* adaptee;public:Adapter(AbstractAdaptee* adaptee) : adaptee(adaptee) {}void request() const override {adaptee->doSomething();}
};// 客户端代码
int main() {AbstractAdaptee* adaptee1 = new Adaptee1();AbstractAdaptee* adaptee2 = new Adaptee2();Target* adapter1 = new Adapter(adaptee1);Target* adapter2 = new Adapter(adaptee2);adapter1->request(); // 调用适配器的方法adapter2->request(); // 调用适配器的方法delete adaptee1;delete adaptee2;delete adapter1;delete adapter2;return 0;
}
2. 使用代理对象(Proxy Object)
代理对象是通过创建一个代理类来间接访问Adaptee的方法。适配器类持有这个代理对象的引用,并通过代理对象来调用Adaptee的方法。这种方式可以提供额外的控制和功能,比如访问控制、缓存等。
UML 类图
+-------------------+ +-------------------+ +-------------------+
| Target | | Adapter | | Proxy |
|-------------------| |-------------------| |-------------------|
| + request() |<----------|>+ request() |<----------|>+ doSomething() |
+-------------------+ |-------------------| +-------------------+| - proxy: Proxy ||-------------------|+-------------------+^||+-------------------+ +-------------------+| Proxy | | Adaptee ||-------------------| |-------------------|| + doSomething() |<----------|>+ doSomething() |+-------------------+ +-------------------+
C++ 代码示例
#include <iostream>// Target 接口
class Target {
public:virtual void request() const = 0;
};// Adaptee 类
class Adaptee {
public:void doSomething() const {std::cout << "Adaptee: Doing something." << std::endl;}
};// Proxy 类
class Proxy {
private:Adaptee* adaptee;public:Proxy(Adaptee* adaptee) : adaptee(adaptee) {}void doSomething() const {std::cout << "Proxy: Before calling Adaptee." << std::endl;adaptee->doSomething();std::cout << "Proxy: After calling Adaptee." << std::endl;}
};// Adapter 类
class Adapter : public Target {
private:Proxy* proxy;public:Adapter(Proxy* proxy) : proxy(proxy) {}void request() const override {proxy->doSomething();}
};// 客户端代码
int main() {Adaptee* adaptee = new Adaptee();Proxy* proxy = new Proxy(adaptee);Target* adapter = new Adapter(proxy);adapter->request(); // 调用适配器的方法delete adaptee;delete proxy;delete adapter;return 0;
}
3. 参数化适配器(Parameterized Adapter)
参数化适配器通过将Adaptee的方法参数化,使得适配器可以在运行时传递不同的参数来调用Adaptee的方法。这种方式可以增强适配器的灵活性。
UML 类图
+-------------------+ +-------------------+
| Target | | Adapter |
|-------------------| |-------------------|
| + request() |<----------|>+ request() |
+-------------------+ |-------------------|| - adaptee: Adaptee||-------------------|+-------------------+^||+-------------------+| Adaptee ||-------------------|| + doSomething(int)|+-------------------+
C++ 代码示例
#include <iostream>// Target 接口
class Target {
public:virtual void request() const = 0;
};// Adaptee 类
class Adaptee {
public:void doSomething(int param) const {std::cout << "Adaptee: Doing something with parameter " << param << "." << std::endl;}
};// Adapter 类
class Adapter : public Target {
private:Adaptee* adaptee;int param;public:Adapter(Adaptee* adaptee, int param) : adaptee(adaptee), param(param) {}void request() const override {adaptee->doSomething(param);}
};// 客户端代码
int main() {Adaptee* adaptee = new Adaptee();Target* adapter = new Adapter(adaptee, 42);adapter->request(); // 调用适配器的方法delete adaptee;delete adapter;return 0;
}
总结
这三种方式都可以在窄接口实现中使用,根据具体的需求选择不同的实现方式。抽象实现提供了灵活的适配能力,代理对象提供了额外的控制和功能,参数化适配器增强了适配器的灵活性。每种方式都有其适用的场景,选择合适的方式可以提高代码的可维护性和扩展性。
相关文章:
C++适配器模式之可插入适配器的实现模式和方法
可插入适配器与Adaptee的窄接口 在C适配器模式中,可插入适配器(Pluggable Adapter)是指适配器类的设计允许在运行时动态地插入不同的Adaptee对象,从而使适配器具有灵活性和可扩展性。这种设计使得适配器不仅限于适配一个特定的Ad…...
每日一练:【动态规划算法】斐波那契数列模型之第 N 个泰波那契数(easy)
1. 第 N 个泰波那契数(easy) 1. 题目链接:1137. 第 N 个泰波那契数 2. 题目描述 3.题目分析 这题我们要求第n个泰波那契Tn的值,很明显的使用动态规划算法。 4.动态规划算法流程 1. 状态表示: 根据题目的要求及公…...
Hash table类算法【leetcode】
哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素 那么哈希表能解决什么问题呢,一般哈希表都是用来快速判断一个元素是否出现集合里。 例如要查询一个名字是否在这所学校里。 要枚举的话时间复杂度是O(n),但如果使用哈希…...
windows实现VNC连接ubuntu22.04服务器
最近弄了一个700块钱的mini主机,刷了ubuntu22.04系统,然后想要在笔记本上通过VNC连接,这样就有了一个linux的开发环境。最后实现的过程为: 安装vnc服务器 安装 VNC 服务器软件: sudo apt update sudo apt install t…...
中国电信星辰大模型:软件工厂与文生视频技术的深度解析
在科技日新月异的今天,人工智能(AI)技术正以惊人的速度改变着我们的生活和工作方式。作为这一领域的领军企业之一,中国电信凭借其强大的研发实力和深厚的技术积累,推出了星辰大模型,旨在为用户带来更加智能、高效、便捷的服务体验。本文将重点介绍中国电信星辰大模型中的…...
项目实战:基于Vue3实现一个小相册
相册的示例效果图 注意看注释... 要实现图片的相册效果,图片命名可以像{img1.jpg,img2.jpg,img3.jpg}类似于这种的命名方式。 CSS部分: <style>/* 伪元素选择器,用于在具有clear_ele类的元素内部的末尾添加一个新的元素 */.clear_ele:…...
macOS安装nvm node
macOS安装nvm macOS安装nvm创建 nvm 工作目录配置环境变量使用 nvm查看可用的 Node.js 版本安装特定版本 macOS安装nvm brew install nvm创建 nvm 工作目录 mkdir ~/.nvm配置环境变量 vim ~/.zshrc# nvm export NVM_DIR"$HOME/.nvm" [ -s "/opt/homebrew/opt…...
解决整合Django与Jinja2兼容性的问题
提问 解决整合Django与Jinja2时遇到了一些兼容性问题。已经按照常规步骤在我的settings.py中配置了Jinja2作为模板引擎,同时保留了Django默认的模板设置。然而尝试同时使用Django和Jinja2时,系统报错提示我没有指定模板。如果我尝试移除Django的默认模板…...
Elasticsearch面试内容整理-高级特性
Elasticsearch 提供了一系列高级特性,这些特性可以极大地增强其搜索、分析和管理能力,使得它在大数据场景中表现出色。以下是 Elasticsearch 的一些重要高级特性: 近实时搜索(Near Real-Time Search) Elasticsearch 的一个关键特性是 近实时搜索(NRT),这意味着数据写入…...
linux通过手工删除文件卸载oracle 11g rac的具体步骤
在linux操作系统中,有些时候我们自己学习和测试会临时搭建的oracle rac。事情完成后,我们想回收资源,需要去卸载oracle rac。为了快速卸载oracle rac,今天我们介绍下如何通过手工删除文件的方式来完成工作(操作都需要在…...
【ArcGISPro】根据yaml构建原始Pro的conda环境
使用场景 我们不小心把原始arcgispro-py3的conda环境破坏了,我们就可以使用以下方法进行修复 查找文件 在arcgis目录下找到yaml文件 如果没找到请复制以下内容到新的yaml文件 channels: - esri - defaults dependencies: - anyio=4.2.0=py311haa95532_0 - appdirs=1.4.4=p…...
刷题笔记15
问题描述 小M和小F在玩飞行棋。游戏结束后,他们需要将桌上的飞行棋棋子分组整理好。现在有 N 个棋子,每个棋子上有一个数字序号。小M的目标是将这些棋子分成 M 组,每组恰好5个,并且组内棋子的序号相同。小M希望知道是否可以按照这…...
【LeetCode热题100】队列+宽搜
这篇博客是关于队列宽搜的几道题,主要包括N叉树的层序遍历、二叉树的锯齿形层序遍历、二叉树最大宽度、在每个数行中找最大值。 class Solution { public:vector<vector<int>> levelOrder(Node* root) {vector<vector<int>> ret;if(!root) …...
【阵列信号处理】相干信号和非相干信号生成
文章目录 一、总结二、知识点相干(coherent)和非相干(incoherent)信号相干信号生成代码 相关信号(correlated signal)相关信号生成代码 正交信号定义 本文记录博主的科研日记。如果对博主的其他文章感兴趣&…...
React 组件生命周期
React 组件生命周期 React 组件生命周期是React框架中一个核心概念,它描述了一个组件从创建到销毁的过程。理解组件生命周期对于高效开发React应用至关重要,因为它允许开发者在一个组件的不同阶段执行特定的逻辑。本文将详细介绍React组件的生命周期方法,并解释它们在组件的…...
Kylin Server V10 下基于Sentinel(哨兵)实现Redis高可用集群
一、什么是哨兵模式 Redis Sentinel 是一个分布式系统,为 Redis 提供高可用性解决方案。可以在一个架构中运行多个 Sentinel 进程(progress)这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线信息,并使用投票协议(agreement protocols)来决定是否执行…...
07-Making a Bar Chart with D3.js and SVG
课程链接 Curran的课程,通过 D3.js 的 scaleLinear, max, scaleBand, axisLeft, axisBottom,根据 .csv 文件生成一个横向柱状图。 【注】如果想造csv数据,可以使用通义千问,关于LinearScale与BandScale不懂的地方也可以在通义千…...
硅谷甄选前端项目环境配置笔记
此教程来自于尚硅谷 文章目录 **此教程来自于尚硅谷**硅谷甄选运营平台一、搭建后台管理系统模板1.1项目初始化1.1.1环境准备1.1.2初始化项目 1.2项目配置一、eslint配置1.1vue3环境代码校验插件1.2修改.eslintrc.cjs配置文件1.3.eslintignore忽略文件1.4运行脚本 二、配置**pr…...
6.7机器学习期末复习题
空间 样本空间 就是属性的所有可能情况,包括了一切可能出现或不可能出现的所有样本情况 版本空间&假设空间 假设空间就是在样本空间的基础上,给所有属性都加了一个通配符,表示任意即可;以及加上了一个空集,表示…...
1123--日期类
目录 一 java 1. Date类 2. calendar类 3. 第三代日期类‘ 3.1 常用方法 3.2 格式化操作 一 java 1. Date类 2. calendar类 3. 第三代日期类‘ 3.1 常用方法 3.2 格式化操作...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
