当前位置: 首页 > java >正文

选择单例还是依赖注入

在大多数软件架构中,推荐使用依赖注入(即在构造函数或工厂里显式传递服务对象),而不是把服务写成全局的单例。这样做有几个好处:

1. 可测试性和可维护性

  • 构造函数注入(Constructor Injection)让每个命令的依赖关系在类接口上就能一目了然,不会隐藏在某个 Service::instance() 调用里;

  • 单元测试时,只要把一个模拟(Mock)或桩(Stub) 服务对象传进来,就能隔离硬件或底层逻辑,做快速的功能级测试;

  • 如果用单例,你很难在测试里把它替换成假对象,也容易遗留跨测试用例的状态污染。

class MotorController;  // 抽象接口class StartMotorCommand : public ICommand {
public:StartMotorCommand(std::shared_ptr<MotorController> svc): svc_(std::move(svc)) {}void execute() override { svc_->start(); }
private:std::shared_ptr<MotorController> svc_;
};

2. 生命周期和线程安全

  • 当你显式管理服务的创建和销毁(比如在应用启动时集中 new,一组命令共享同一个 shared_ptr),就能清楚知道它的作用域和销毁时机;

  • 单例往往在首次访问时才创建,也可能依赖静态初始化顺序,容易引发隐藏的初始化竞态(尤其是在多线程环境下);

3. 何时可考虑单例?

尽管单例有上述弊端,但在以下场景下也能适度使用:

  • 全局硬件资源——比如整机只有一套通信总线接口,创建多个实例浪费资源;

  • 第三方库只允许单实例——某些底层 SDK 本身就是单例设计,强制做成全局访问;

  • 非常简单、不可替换的服务——几乎不会有模拟需求,且接口非常稳定、无状态。

如果你确实要用单例,推荐:

  • 给它一个清晰的线程安全接口,使用 C++11 的局部静态变量或 std::call_once 实现;

  • 不要在单例里产生跨模块的全局可变状态;

  • 在单例类上也提供抽象接口(继承于 MotorController),方便将来用 DI 替换或 Mock。

class MotorControllerSingleton : public MotorController {
public:static MotorControllerSingleton& instance() {static MotorControllerSingleton inst;return inst;}void start() override { /* … */ }
private:MotorControllerSingleton() = default;// 禁止拷贝/移动MotorControllerSingleton(const MotorControllerSingleton&) = delete;MotorControllerSingleton& operator=(const MotorControllerSingleton&) = delete;
};

4. 小结

  • 首选依赖注入:传 shared_ptr / unique_ptr / 原始指针,通过构造函数或工厂组装,代码可读、可测、易维护;

  • 仅在实在无法用 DI(或第三方限制)时,才退而求其次选用单例模式,并严格控制可变状态和线程安全。

http://www.xdnf.cn/news/5855.html

相关文章:

  • 【每天一个知识点】Dip 检验(Dip test)
  • CSS经典布局之圣杯布局和双飞翼布局
  • spark-cache模式
  • ubuntu22.04编译PX4无人机仿真实践
  • EMQX v5.0通过连接器和规则同步数据
  • PyInstaller 打包后 Excel 转 CSV 报错解决方案:“excel file format cannot be determined“
  • 【LUT技术专题】SPFLUT代码解读
  • Mirror的多人连接管理及房间系统
  • github 上的 CI/CD 的尝试
  • 掌握Multi-Agent实践(五):基于KIMAs的多智能体知识集成系统构建与应用实践
  • 每日算法刷题计划Day5 5.13:leetcode数组3道题,用时1h
  • AFFS2 的 `yaffs_ext_tags` 数据结构详解
  • 大模型MCP_MCP从流式SSE到流式HTTP_1.8.0支持流式HTTP交互_介绍_从应用到最优--人工智能工作笔记0245
  • C++修炼:继承
  • API的学习总结(上)
  • # 08_Elastic Stack 从入门到实践(八)---1
  • 每日Prompt:发光线条解剖图
  • 生信小白学Rust-03
  • 机器学习之决策树模型:从基础概念到条件类型详解
  • 【WIN】笔记本电脑忘记密码解决办法/笔记本电脑重装系统笔记/bitlocker忘记密码的解决办法
  • UDS诊断----------$27诊断服务
  • BFS算法篇——从晨曦到星辰,BFS算法在多源最短路径问题中的诗意航行(上)
  • 3.1 泰勒公式出发点
  • 人脸识别门禁系统技术文档
  • 运行Spark程序-在shell中运行 --SparkConf 和 SparkContext
  • Hadoop和Spark生态系统
  • Java详解LeetCode 热题 100(15):LeetCode 189. 轮转数组(Rotate Array)详解
  • 跨境电商定价革命:亚马逊“逆向提价“策略背后的价值重构逻辑
  • 鸿蒙接入flutter环境变量配置windows-命令行或者手动配置-到项目的创建-运行demo项目
  • (七)深度学习---神经网络原理与实现