C++ 单例设计模式替代方案

2024-03-23

我讨厌死马当活马医,也就是说,在过去的几天里,我已经阅读了很多关于单例模式使用的相互冲突的文章。

这个问题不是关于哪个是一般更好的选择,而是关于什么对我的用例有意义。

我正在做的宠物项目是一个游戏。我目前正在编写的一些代码,我倾向于使用单例模式。

用例如下:

  • 全球可访问的记录器。
  • OpenGL 渲染管理器。
  • 文件系统访问。
  • 网络访问。
  • etc.

现在需要澄清的是,以上多个要求在访问之间共享状态。例如,记录器正在包装日志库并需要指向输出日志的指针,网络需要已建立的开放连接等。

现在据我所知,更建议避免单例,所以让我们看看我们如何做到这一点。许多文章只是说在顶部创建实例并将其作为参数传递到需要的任何地方。虽然我同意这在技术上是可行的,但我的问题是,如何管理潜在的大量参数?那么想到的是将不同的实例包装在一种“上下文”对象中并传递它,然后执行类似的操作context->log("Hello World")。现在可以肯定这还不错,但是如果您有这样的框架怎么办:

game_loop(ctx)
   ->update_entities(ctx)
        ->on_preupdate(ctx)
             ->run_something(ctx)
                 ->only use ctx->log() in some freak edge case in this function.
        ->on_update(ctx)
            ->whatever(ctx)
                 ->ctx->networksend(stuff)
   ->update_physics(ctx)
        ->ctx->networksend(stuff)
        //maybe ctx never uses log here.

你明白了......在某些领域,“ctx”的某些方面从未被使用过,但你仍然坚持在任何地方传递它,以防你可能想使用记录器或稍后调试某些东西在开发过程中,您实际上需要网络或该代码部分中的任何内容。

我觉得上面的例子更适合全局可访问的单例,但我必须承认,我来自 C#/Java/JS 背景,这可能会影响我的观点。我想采用 C++ 程序员的思维方式/最佳实践,但正如我所说,我似乎无法找到直接的答案。我还注意到,建议仅传递“单例”作为参数的文章仅给出了非常简单的用例,任何人都同意参数将是更好的方法。

在此游戏示例中,即使您不打算立即使用日志记录,您也可能不想在任何地方访问日志记录。文件系统的东西可能已经全部结束了,但是在您构建出项目之前,很难说它何时何地最有用。

So do I:

  1. 无论人们说它多么“邪恶/坏”,都坚持在这些用例中使用单例。
  2. 将所有内容包装在上下文对象中,并将其逐字传递到任何地方。 (在我看来有点恶心,但如果这是“更容易接受/更好”的做法,那就这样吧。)
  3. 完全是另一回事。 (真的不知道那可能是什么。)

如果选项 1,从性能的角度来看,我是否应该切换到使用命名空间函数,并将“私有”变量/函数隐藏在匿名命名空间中,就像大多数人在 C 中所做的那样? (我猜性能会有一个小小的提升,但随后我将不得不对其中一些方法调用“init”和“destroy”方法,而不是仅仅允许构造函数/析构函数为我这样做,仍然值得吗?)

现在我意识到这可能有点基于意见,但我希望当出现更复杂/嵌套的代码库问题时我仍然可以获得相对好的答案。

Edit:经过深思熟虑,我决定改用“服务定位器”模式。为了防止服务定位器的全局/单例,我使任何可能使用服务的东西继承自抽象基类,该基类需要在构造时传递服务定位器。

我还没有实现所有功能,所以我仍然不确定这种方法是否会遇到任何问题,并且仍然希望得到关于这是否是单例/全局范围困境的合理替代方案的反馈。

我读过,服务定位器在某种程度上也是一种反模式,也就是说,我发现的许多示例都使用静态和/或作为单例来实现它,也许按照我所描述的方式使用它可以消除导致它的方面是一个反模式?


每当您认为想要使用 Singleton 时,请问自己以下问题:为什么必须不惜一切代价确保在任何时间点都不存在该类的多个实例?因为单例模式的全部意义在于确保永远不会有多个单例实例。这就是“单例”一词的含义:只有一个。这就是为什么它被称为单例模式。这就是为什么该模式要求构造函数是私有的。单例模式的要点是not从来没有给你一个全局可访问的实例。唯一实例有一个全局访问点这一事实只是单例模式的结果。这不是单例模式想要实现的目标。如果您想要的只是某个东西的全局可访问实例,那么请使用全局变量。这正是全局变量的用途……

单例模式可能是一种最常被误解的设计模式。一次只能有一个网络连接,如果违反这一约束,世界就会走向末日,这是否是网络连接概念的一个内在方面?如果答案是否定的,那么就没有理由将网络连接建模为单例。但不要相信我的话,请查看第 127 页来说服自己设计模式:可重用的面向对象软件的元素单例模式最初被描述的地方......????

关于你的例子:如果你最终不得不将大量参数传递到某个地方,那么首先最重要的是告诉你一件事:那个地方有太多的责任。使用单例并不会改变这一事实。单例的使用只是混淆了这一事实,因为您不必被迫以参数的形式通过一扇门传递所有内容,而是直接在整个地方访问您想要的任何内容。但你仍然可以访问这些东西。所以你的代码段的依赖关系是相同的。这些依赖关系只是不再在某些接口级别上明确表达,而是在迷雾中蔓延。而且你永远不会预先知道某段代码依赖于什么东西,直到你的构建在尝试删除其他东西碰巧依赖的东西后崩溃的那一刻。请注意,此问题并非单例模式所特有。一般来说,这是任何类型的全球实体都关心的问题……

因此,您不应该问如何最好地传递大量参数,而应该问为什么这段代码需要访问这么多东西?例如,您真的需要显式地将网络连接传递到游戏循环吗?如果游戏循环不应该只知道物理世界对象,并且该物理世界对象在创建时就给出了处理网络通信的某个对象。该对象又在初始化时告诉它应该使用的网络连接?日志可能只是一个全局变量(或者日志本身的想法是否真的禁止存在多个日志?)。或者,也许每个线程拥有自己的日志(可能是线程局部变量)实际上是有意义的,这样您就可以按照线程恰好采用的控制流的顺序从每个线程获取日志,而不是某些(在最好)交错的混乱,这将是多个线程的输出,您可能想为其编写一些工具,以便您至少有一些希望能够理解它......

关于性能,请考虑一下,在游戏中,您通常会有一些父对象,每个父对象都管理小型子对象的集合。性能关键的事情通常发生在必须对此类集合中的所有子对象执行某些操作的地方。第一次访问父对象本身的相对开销通常可以忽略不计......

PS:你可能想看看实体组件系统模式 https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 单例设计模式替代方案 的相关文章

随机推荐

  • 在Python中从pdf中提取印地语编写的文本[重复]

    这个问题在这里已经有答案了 我想从 pdf 文档中提取印地文输入的文本 我已附上示例页面的图像 https i stack imgur com UkT86 jpg我正在处理 我尝试使用 pdfminer 从中获取文本 但文本是乱码 可能是由
  • 悬停时的 jQuery 卡片动画

    当鼠标悬停在其中一张卡片上时 其余卡片应正确地逐一减小大小 例如 将鼠标悬停在卡片 1 上 目前我的解决方案是一一添加和删除类 但我想知道是否有更好的方法使用 jQuery 来完成它 card mouseover function card
  • 超出低磁盘水位线 [??%]

    我在我的开发机器 一台笔记本 中使用 Elasticsearch 1 4 4 一切都设置为默认值 因为我从未更改过任何设置 当我启动它时 我通常会收到以下消息 2015 10 27 09 38 31 588 INFO node Milan
  • Android 应用程序关闭时如何保持 Web 套接字连接处于活动状态

    我正在寻找一个应用程序 向 websocket 发送消息 以便在计算机内的特定参数发生变化时启用通知 参数在计算机中发生变化 通过 websocket 传输 然后到达我的应用程序 这部分是正确的 对吧 即使我的应用程序关闭 我如何接收这些
  • 如何在 Linux (Raspberry Pi) 上从帧缓冲区获取像素颜色

    我正在尝试编写一个小程序来根据屏幕上某些像素的颜色来控制 RGB LED 的颜色 由于这是在运行 Raspbmc 的 Raspberry Pi 上 我无法使用 XLib 因为所有内容都是从帧缓冲区中绘制的 不确定这是否属实 但从我在常见问题
  • 禁用功能区按钮

    我想禁用而不是隐藏特定列表类型上的功能区按钮 特别是 Ribbon ListItem New NewListItem 在网络上 我可以找到很多帖子向我展示删除 隐藏按钮的方法 但没有一个真正有价值的帖子可以告诉我如何在自定义列表类型中禁用现
  • Streamlit:如何使用按钮重新加载页面并在每次单击后存储以前的信息

    我的应用程序的目的是在填写表格后构建一个 可下载的 tsv 文件 每个表格页面都与新学生的信息相关 所以我总是有相同的页面结构 每个学生都有相同的表格 我想要的是添加一个按钮 为每个新学生生成一个新页面 最后 结束 按钮应显示表格 例如使用
  • 如何增加 Visual Studio 中的错误限制?

    在 Visual Studio 中构建应用程序时 当超过错误计数时显示以下错误 fatal error C1003 error count exceeds 100 stopping compilation 有没有办法提高错误限制 此限制是硬
  • 在 Node 中以编程方式创建证书和证书密钥

    使用 node js 我想编写代码以编程方式执行与以下操作等效的操作 openssl genrsa des3 passout pass x out server pass key 2048 openssl rsa passin pass x
  • 引起原因:java.lang.NoClassDefFoundError:com/fasterxml/jackson/databind/JsonMappingException$Reference

    我对 Java 完全陌生 我正在尝试显示 JSON 数据 为此我决定使用 Jackson 库 但我收到错误 我在用 jackson annotations 2 3 0 jar jackson core 2 4 2 jar jackson d
  • 如何在材料表中使用自定义“editComponent”?

    我正在尝试在我的项目中使用 材料表 我的 图标 列包含图标名称 我需要通过从外部对话框中选择它来更改此图标 我在从外部对话框更新表数据时遇到问题 当我使用 输入 元素并更改图标名称时 它可以正常工作 editComponent props
  • 如何使用堆栈安装包?

    使用 cabal 我可以使用以下命令安装 hakyll cabal install hakyll 我怎样才能使用堆栈做同样的事情 stack install hakyll stack提供了一套精选的软件包 不会炸毁您的机器 如果您想检查哪些
  • Lambda 似乎不适用于 VS2010 中的引用类

    Visual Studio 2010 中最酷的 C 新功能之一是 lambda 表达式 但是 我无法让他们在托管类中工作 class UnmanagedClass void Foo Creating empty lambda within
  • 在 jboss 7 中使用 java 服务包装器时出现问题

    我正在尝试使用 Java 服务包装器使用此配置将 Jboss 7 1 0 Final 安装为服务 这是一个草案conf 因此请忽略绝对路径 Java Application wrapper java command java wrapper
  • 扩展Page类

    是否可以扩展 C 的 WPF 工具包中的 Page 类 或任何其他 WPF 类 我尝试做的事情 public class ExtendedPage Page protected void doStuff lots of joy n plea
  • 使用java查找堆栈的索引(仅使用push、pop和peek方法)

    新程序员在这里学习java 我试图计算一个循环需要多少步才能停止考虑用户输入的内容 请记住 学校里的某些人设置了限制sigh您只能使用堆栈中的某些方法 这些方法是push pop 和peek Input first input是添加了多少元
  • 锁定分支,使其无法暂存/提交? (仅合并/等)

    我有一个master develop我喜欢的分支系统 但它有一个基本规则 没有提交develop or master 仅合并 这很棒 但最近我不小心在我的开发分支上做了一些更改 提交 这让我很恼火 我考虑将这些提交的所有权从develop进
  • Qt 应用程序抛出“dyld:未找到符号:__cg_jpeg_resync_to_restart”

    我变得众所周知dyldOS X 上的问题 Qt pro文件 INCLUDEPATH usr local Cellar libpng 1 6 23 include usr local Cellar jpeg 8d include LIBS L
  • 用于密码存在验证的自定义错误消息

    看起来这应该很简单 注册新用户时 我想要空白用户名和密码的自定义错误 对于用户名来说效果很好 validates name presence message Please enter a name length maximum 50 mes
  • C++ 单例设计模式替代方案

    我讨厌死马当活马医 也就是说 在过去的几天里 我已经阅读了很多关于单例模式使用的相互冲突的文章 这个问题不是关于哪个是一般更好的选择 而是关于什么对我的用例有意义 我正在做的宠物项目是一个游戏 我目前正在编写的一些代码 我倾向于使用单例模式