游戏引擎开发中Singleton与传递Application引用方案对比及命名咨询
方案优劣对比
单例方案
- 优势
- 无需逐层传递引用,深层组件可直接调用
Application::getInstance()获取实例,编码成本极低 - 无需修改现有类的构造函数签名,改造成本极低
- 无需逐层传递引用,深层组件可直接调用
- 劣势
- 强绑定全局状态,完全无法做单元测试,不能Mock Application实例验证组件逻辑
- 依赖隐式化,仅看组件代码无法感知到它对Application的依赖,问题排查成本极高
- 后续扩展性极差,如果要做多实例编辑器、多窗口并行运行等功能,单例完全不支持
- 生命周期不可控,程序退出时销毁顺序容易出问题,大概率会出现野指针访问崩溃
手动传递+继承基类方案
- 优势
- 依赖完全显式,所有需要访问Application的类都有明确的继承标记,依赖关系一目了然
- 无全局状态,单元测试可传入自定义的Application实例,支持多实例场景扩展
- 生命周期可控,只要保证所有持有引用的组件在Application销毁前释放,就不会出现野引用
- 减少重复代码,无需每个需要访问Application的类单独声明引用成员、编写初始化逻辑
- 劣势
- 初期改造成本高,深层组件需要Application引用时,要修改所有上层节点的构造函数传参
- 引入了继承关系,如果组件类已有其他父类,会带来C++多继承的额外复杂度
- 所有继承类都和Application强耦合,后续如果要拆分Application的功能模块,所有相关类都要调整
其他可行方案
服务定位器模式
实现一个轻量的全局服务容器,将Application拆分出的子模块(输入、渲染、日志等)注册到容器中,组件仅按需获取对应的服务实例,不需要依赖完整的Application。
- 优势:比直接用单例灵活,支持替换服务实现,耦合度远低于依赖完整Application
- 劣势:仍存在全局状态,依赖还是隐式的,问题排查成本高于显式传参
细粒度依赖传递
不传递完整的Application引用,组件需要什么能力就传对应模块的引用,比如需要输入就传InputSystem&,需要日志就传Logger&。
- 优势:耦合度最低,每个类仅依赖自己需要的接口,单元测试成本最低
- 劣势:需要提前拆分Application的各个功能模块,初期开发工作量大
纯显式依赖注入
不使用公共基类,每个需要依赖的类自行在构造函数中声明需要的参数,由上层创建实例时传入。
- 优势:无继承负担,不会引入多继承问题
- 劣势:每个类都要重复写引用声明、初始化逻辑,重复代码多
基类命名建议
如果确定使用该基类方案,最符合业内开发规范的命名是ApplicationAware。Aware后缀是通用命名惯例,明确表示该类的子类「可以感知、访问Application实例」,语义清晰且符合多数开发框架的命名习惯。
如果你的项目风格更偏向直白命名,也可以选择ApplicationReferenceHolder,但长度更长,通用性不如前者。
内容的提问来源于stack exchange,提问作者user3621272
相关产品推荐
相关产品推荐

