单例模式实现的关注点无外乎:
- 构造函数必须有private访问权限,这样才能避免被new
- 对象创建时是否线程安全
- 是否支持延迟加载
- GetInstance的性能
具体的实现方案可参见see: Best Practice of Unity Singleton
但是不建议在项目中滥用单例模式。
-
单例模式隐藏类之间的依赖关系
不好知道一个类究竟依赖了哪些单例类,单例是面向过程的设计风格
-
单例模式影响代码的拓展性
万一将来有一天想变单例为实例……
-
影响了代码的可测试性
主要是写单元测试代码的时候
-
单例模式不支持包含参数的构造方法
虽可通过提供Init()方法/将参数放在Instance()函数中/将参放在全局变量中来实现,但又引发各自的新问题
-
考虑是否需要这个类
-
依赖注入我不觉得这是好的方案,许多情况传递层级会太深了
-
将类限制为单一实例(在构造函数里用assert)我不觉得这是好方案,单例在编译器就能检查实例个数,这个方案在运行时才能检查
-
静态方法
- 一些情况下这是个比较好的方案,比如Unity对于Log就没有设计成单例类,而是静态方法。如果一个单例里功能不多,可能是考虑用静态方法的时候。
- 但这也不是银弹,它依旧难以扩展,测试不方便
-
ScriptObject
Unity里,配置相关的单例我主张用ScriptObject来做
-
使用服务定位器模式
这是推荐做法,参照ToLua框架的AppFacade类
关于服务定位器的更多介绍,参见《TODO:》
单例模式的最大问题就是它影响了代码的可拓展性。
综合考虑,我们不需要在游戏里实现单例。
管理类通过服务定位器来调用。
配置类用ScriptObject来实现。
一些通用的方法掉用靠静态类的静态方法来调用。
- 《设计模式之美》第6章
- 《游戏编程模式》第6章