单例模式的应用

本文探讨了单例模式在游戏开发中的应用,区分了资源对象和实例对象,强调了如何恰当使用单例以减少资源浪费。文章指出,无状态工具和静态配置数据适合使用单例,而涉及多线程或者需要跨调用上下文的对象则应谨慎使用。单例的主要优势在于提供共享资源的便捷访问。同时,文章提到了在特定的线程模型下,单例在特定调用上下文内的使用是可接受的,但应避免单例之间的依赖关系导致的复杂性。
摘要由CSDN通过智能技术生成

单例模式的应用

链接:https://www.zhihu.com/question/29691758/answer/47356233
来源:知乎

我觉得要想用好单例,最重要的是想怎么样用少单例。越少越好。
游戏软件处理的对象,一般分“资源对象”与“实例对象”。
“资源对象”:在程序运行时,储存一种游戏资源的数据。这个对象内的数据用于游戏程序中的渲染,声音播放等。一般而言,资源对象内的资源数据在程序运行时很少变动。比如贴图,3D网格,音频数据等。资源数据往往比较占内存空间。 资源对象可以被多个“实例对象”所共享。以节约内存。
“实例对象”:代表游戏世界中的任意类型的实体。这个实体有自己的一些属性。并且在游戏时经常会变动。实例对象内部含有一个其共享的“资源对象”的索引(指针)。这篇文章的这部分我觉得总结的很对,而我对用不用单例的情景划分也是以这个为主要依据的。

有些情景是不得不用单例的:
比如各种无状态Utils,各种辅助函数。本来就是无状态的,还实例化的话简直就不可理喻了。比如配表数据,当然前提你的配表转数据方案是直接转成代码,这种是静态数据,无并发问题,最好单例。有些情景是用单例更方便的:
像bundle管理器、声音/音效管理器这种,按文章里的划分,是属于资源对象,是一定在渲染线程中调用的,而且并不会影响gameplay,单例用起来代码字数少很多,何乐不为。像一些全局的状态管理,跟具体scene无关的,单例也可。还有就是unity帮你确保线程安全的一些东西,比如log这些的。

那么什么情景下不建议用单例呢?
最简单的,需要跨调用上下文的实例最好不要做成单例。
一般情况用unity写的游戏是无需考虑多线程问题的,基本都是跑在渲染线程的,大家都在直接接触各种GameObject,但是如果你的游戏要支持一些比较特殊的功能,比如重放、比如边玩边更新、比如windows上要锁屏断线重连,那你的逻辑主循环放在渲染线程中是很要命的,写的时候蛋疼不说还会出各种意想不到的bug。
这个时候你相当于要通过一个逻辑线程来驱动整个游戏逻辑,渲染线程只是拿到数据并画出来。这样也更符合游戏开发的模式,摘掉了用unity写游戏=小作坊的帽子。还有一种情况,就是很多单例之间总会产生依赖的,有依赖那就需要你有人肉维护的创建/销毁顺序严格的一坨逻辑,这种肯定没有非单例的对象依赖树看的那么直白。比如我有一套运行时推翻重来的逻辑,后来人新加一个单例,顺序没加好,动不动就能让运行时推翻重来这类逻辑爆掉,开发期没爆掉那就更可怕了。

单例的优势究竟是什么?
同一个调用上下文内,只有这么一份的某个东西S,我有很多实例需要S,但是这些实例不需要都存对S的一份引用,可以通过一个约定好的入口直接拿到S。
其他的优势我真的想不到了,有想到的朋友请补充。

举个实际例子
还是以之前说的逻辑线程+渲染线程模式为基础:
为了从根本上避免逻辑线程访问到渲染对象,我们的gameplay全放在一个assembly里。机制来确保这个assembly的gameplay一定是跑在同一个调用上下文中的。这个assembly内部,不会出现race condition。那对于gameplay部分来说,assembly internal的单例是可以接受的,也基本上是只要约定好初始化顺序(销毁不考虑),典型如网络模块和gameplay实体管理模块创建时间就是差很多的。public的单例肯定是不能接受的,只要你public了,团队里一定会有人去用你的这个单例的。
对于Assembly-CSharp这个assembly里的逻辑来说,单例用的最多的应该还是我之前提到过的不得不用的情况和用了更方便的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值