c++ - 在 C++ 中为许多类设计持久性模型同时确保遵循 SOLID 的最佳方法是什么?
问题描述
我已经在网上搜索了有关如何有效地为 C++ 类设计持久性模型的特定设计信息,但我发现不足。所以我决定在这里问。
假设我有 4 个课程:
A级
B类
C类
类坚持
我想使用“Persist”类将 A、B 和 C 类持久保存到磁盘,这样一个文件包含每个类的配置信息:
my_module.json
{
A {
...
},
B {
...
},
C {
...
}
}
我的问题是,按照 SOLID 原则进行设计的最佳方法是什么?
例如,单一责任原则建议一个类应该只有一种责任(或只有一个改变的理由),而得墨忒耳法则建议类之间尽可能少地了解彼此。
那么:
1)一个类应该知道如何序列化自己,还是已经违反了单一职责?
2)如果我使用像“cereal”这样的第三方库来序列化“A类”,我需要为“A类”的内部成员添加标签,以向引擎展示它应该如何序列化。这增加了“A类”和第三方引擎之间的耦合,这一定很糟糕吧?
3) 我是否应该改用中间类,将对象从“A 类”转换为具有第三方库可以理解的适当标签的对象?这从“A 类”中删除了任何关于序列化的知识,但它增加了更多的复杂性和更多的类。
4) 班级应该知道持久性模块吗?尝试序列化时这是否现实?如果是这样,该类应该如何通知持久性模块状态已经改变并且是时候持久化了?或者持久性模块是否应该简单地定期轮询对象以获取新配置?
我对 OO 还很陌生,但很感兴趣。类之间交互的细节将非常有帮助。谢谢。
解决方案
通常在软件工程中,决策不是关于原则,而是关于权衡。
1 - 它违反了单一职责,但在 C++ 中你没有太多选择。该语言本身不支持运行时反射,也不支持虚拟构造函数/类工厂。没有这两个特性,没有对象序列化自己就很难实现对象序列化。“非常困难”是指它需要外部工具或大量宏/模板,IMO 长期支持非常昂贵。
2 - 它增加了耦合,增加了很多复杂性(你必须支持你正在使用的任何 3rd 方库),但是如果你需要同时支持多种不同的序列化格式,例如 JSON、XML 和二进制,它仍然可能是一件好事。
3 - 取决于,但我会说“不”。中间表示的好格式是类似于 DOM 树的东西,你会浪费太多的 CPU 时间来构建它,因为太多的小 RAM 分配和指针追逐。如果出于某种原因您想要设计和支持定义良好的中间表示,我会将其构建为一对事件驱动的接口(一个读者另一个作者),请参阅SAX以获得灵感。
4 — 大多数序列化库的设计使得保存/加载是一个一次性的过程。原因是大多数现代通用序列化格式(XML、JSON、大多数二进制格式)不支持更新。无论如何,您都必须写出完整的文件。只有当存储格式支持部分更新时,通知才会带来价值,例如,如果您在 [嵌入式] 数据库中保存/加载,而不仅仅是在 JSON 文件中。通常的做法是,每次完成对对象的修改后,从外部手动调用 save。使用自动保存很容易浪费太多的 IO 带宽和 CPU 时间在一些不一致的中间状态下写入对象。
推荐阅读
- python - 给定pytorch中的idx列表,如何从数据集中获取一批样本?
- python - 如何在 Windows 启动期间自动运行烧瓶应用程序?
- aurelia - 错误:没有为元素定义 Aurelia API:“DIV”
- javascript - 如何使用 tensorflow JS 在 Javascript 中运行 UNet 分割模型?
- javascript - 如何将变量从 history.replace 传递给组件
- self-extracting - 我可以在 iexpress 自解压器中更新可执行文件的名称吗?
- authentication - .Net Core 身份在 .Net Core 5.0 中添加公司名称作为用户登录过程的一部分
- reactjs - 如何从 React Hooks Form 7.0 中的文本字段中排除特殊字符
- performance - 在 OWASP Benchmark 中生成安全工具之间比较的正确结果的问题
- installation - Openstack 的 Freezer 安装过程中的 Keystone 异常