当前位置: 首页 > news >正文

深入 C++ 实践:如何在完全不改变已有模块架构的情况下,二次封装接口给外部模块使用

往期本博主的 C++ 精讲优质博文可通过这篇导航进行查找:
👇👇👇👇👇
Lemo 的C++精华博文导航:进阶、精讲、设计模式文章全收录
👆👆👆👆👆

前言

前文中我们讲到,如何通过应用 Pimpl 模式进行接口类实现类的分离,来进行接口的设计。

没有读过前文的读者,可以通过这个链接去读下: 《深入 C++ 实践:通过 Pimpl 模式来谈接口类与实现类分离的设计原则》

但是,实际业务场景中是多变的,不可能通过一种方式适用所有场景。

现在我们就来看看一种比较常见的情况:
模块层/平台层代码已经完成了功能开发,但是业务比较复杂,或耦合较深,不便于修改现有接口暴露出来给外部模块使用。想基于已有的功能新增API接口暴露出来供外部使用。

那么对于这种问题,我们的解决思路是什么呢?

解决思路

对于这种问题,我们通常能够想到的是通过协议扩展的形式来解决。

通俗而言,怎么做呢?

封装的类与平台内部的类通过协议扩展形式进行关联,即封装的类和内部的类既不是派生关系,也不是通过指针指向的接口类实现类的关系,而是遵守一套相同的协议进行关联。

即:模块初始化时,内部的类通过协议扩展关联封装的类;模块卸载时,内部的类通过协议扩展与封装的类解除关联;

当调用封装的类时,实际上通过协议扩展去找到内部的内,然后进行相关业务逻辑的流转。

我们本着这样的思路来看一段示例代码:

示例代码

#include <iostream>
#include <map>// 定义协议接口
class IProtocol {
public:virtual void PerformAction() = 0;virtual ~IProtocol() {}
};// 内部类实现协议接口
class InternalClass : public IProtocol {
public:void PerformAction() override {std::cout << "InternalClass action performed." << std::endl;}
};// 协议扩展管理器
class ProtocolExtensionManager {
public:void Register(const std::string& key, IProtocol* protocol) {protocols[key] = protocol;}void Unregister(const std::string& key) {protocols.erase(key);}IProtocol* GetProtocol(const std::string& key) {auto it = protocols.find(key);if (it != protocols.end()) {return it->second;}return nullptr;}private:std::map<std::string, IProtocol*> protocols;
};// 封装类,用户将使用此类,它对内部类进行封装
class WrappedClass {
public:WrappedClass(ProtocolExtensionManager& manager, const std::string& key): protocolManager(manager), protocolKey(key) {}void DoSomething() {IProtocol* internal = protocolManager.GetProtocol(protocolKey);if (internal != nullptr) {internal->PerformAction();} else {std::cout << "Action cannot be performed, no protocol associated." << std::endl;}}private:ProtocolExtensionManager& protocolManager;std::string protocolKey;
};// 示范如何使用上述类
int main() {// 创建协议扩展管理器ProtocolExtensionManager manager;// 创建内部类并注册到管理器中InternalClass internal;manager.Register("InternalProtocol", &internal);// 创建封装类,封装内部类的使用WrappedClass wrapped(manager, "InternalProtocol");// 使用封装类来执行操作wrapped.DoSomething();// 注销协议并清理manager.Unregister("InternalProtocol");return 0;
}

在这个示例中,InternalClass 实现了 IProtocolWrappedClass 是用户互动的封装类,它通过 ProtocolExtensionManager 来调用指定的 InternalClass 实例。

当然,上述的封装还不够彻底。通常对于 WrappedClass 而言,设计者会进行隐藏一系列设计逻辑。那么对于这块,可以采用上文中的 Pimpl 模式,进行接口类和实现类的再次封装,进行信息的隐藏。

再次封装

class WrappedClass {
public:WrappedClass(ProtocolExtensionManager& manager, const std::string& key){APP_I(*new WrappedClassImp(manager, key));}WrappedClass::WrappedClass(WrappedClassImp& impl){APP_I(impl);}WrappedClass::~WrappedClass(){APP_E(); }void DoSomething() {APP_D(WrappedClass);d->DoSomething();}protected:APP_DISABLE_COPY(WrappedClass);APP_DECLARE_IMPL(WrappedClass);
};class WrappedClassImp : public BaseImpl<WrappedClass> {
public:APP_DEFINE_GET_IMPL_FUNC(WrappedClass);WrappedClassImpl(ProtocolExtensionManager& manager, const std::string& key): protocolManager(manager), protocolKey(key) {}void DoSomething() {IProtocol* internal = protocolManager.GetProtocol(protocolKey);if (internal != nullptr) {internal->PerformAction();} else {std::cout << "Action cannot be performed, no protocol associated." << std::endl;}}private:ProtocolExtensionManager& protocolManager;std::string protocolKey;
};

如此,再次封装之后,更多的信息被隐藏了。

总结

这类问题的解决思路有很多,在此先抛砖引玉出一种供大家参考。如果有更好的方式,欢迎留言讨论。

相关文章:

  • 【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 多段线路径压缩(100分)- 三语言AC题解(Python/Java/Cpp)
  • python项目在日志中 打印出详细的请求参数和返回的响应
  • 观成科技:基于深度学习技术的APT加密流量检测与分类检测方案
  • 任务倒计时App
  • 公司面试题总结(二)
  • BC C language
  • 【运维】Ubuntu换硬盘扩容
  • web刷题记录(5)
  • Python网络爬虫4-实战爬取pdf
  • PDF编辑与修正 提高工作效率 Enfocus PitStop Pro 2022 中文
  • Spring应用如何打印access日志和out日志(用于分析请求总共在服务耗费多长时间)
  • Mybatis06-动态SQL
  • K8s 卷快照类
  • WindTerm使用SSH密钥连接阿里云实例,服务器设置SSH密钥登录
  • NIFI启动后密码在哪里
  • __proto__ 和 prototype的关系
  • Git学习与使用心得(1)—— 初始化
  • Java 多线程编程之:notify 和 wait 用法
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • laravel with 查询列表限制条数
  • Mysql优化
  • Puppeteer:浏览器控制器
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • Spring Cloud Feign的两种使用姿势
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • vue.js框架原理浅析
  • vue-router 实现分析
  • vue总结
  • 不上全站https的网站你们就等着被恶心死吧
  • 基于Android乐音识别(2)
  • 基于遗传算法的优化问题求解
  • 记录一下第一次使用npm
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 通过npm或yarn自动生成vue组件
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 你对linux中grep命令知道多少?
  • gunicorn工作原理
  • ​IAR全面支持国科环宇AS32X系列RISC-V车规MCU
  • ​iOS安全加固方法及实现
  • ​queue --- 一个同步的队列类​
  • ​插件化DPI在商用WIFI中的价值
  • #Datawhale AI夏令营第4期#AIGC方向 文生图 Task2
  • #VERDI# 关于如何查看FSM状态机的方法
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (13):Silverlight 2 数据与通信之WebRequest
  • (7)svelte 教程: Props(属性)
  • (arch)linux 转换文件编码格式
  • (C11) 泛型表达式
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (九十四)函数和二维数组
  • (十五)、把自己的镜像推送到 DockerHub