工厂负责创建逻辑,容器决定复用策略,单例/多例是配置结果而非类属性;同一类可依容器配置表现为单例或多例,关键看是否需状态隔离或全局共享。

工厂服务、单例、多例和容器配置不是互斥选项,而是不同层级的协作机制。关键不在“选哪个”,而在“谁管什么”——工厂负责怎么造,容器管造几次,单例/多例是结果而非手段。
工厂服务:只管创建逻辑,不决定复用性
像 CodeIgniter4 的 Services::database() 或 Spring 的工厂方法,本质是封装构造过程:组装参数、选择驱动、设置默认值。它本身不保存状态,也不控制实例是否重复创建。
- 每次调用 Services::email() 都返回新对象 → 适合需隔离状态的场景(如发不同邮件)
- 若在方法内没做缓存,Services::cache() 每次都 new → 实际等价于直接 new,没发挥容器价值
- 它不被容器管理,也不参与依赖注入,只是你写的一组静态工具函数
单例 vs 多例:由容器配置决定,不是类本身的属性
同一个类,可以既是单例也是多例,取决于你怎么告诉容器。
- 单例:容器确保全局唯一实例,适用于配置管理器、日志器、连接池等需要状态一致的组件
- 多例:每次 get() 都新建对象,适用于 Action、DTO、临时处理器等需隔离上下文的场景
- Spring 中通过 scope="singleton"(默认)或 scope="prototype" 显式声明;CI4 中靠 $container->set(...)->shared(true) 控制
容器配置:真正决定生命周期的开关
容器才是统一调度中心。它把工厂创建的实例纳入管理,并按规则复用或重建。
- 用 $container->set('logger', fn() => new FileLogger(...))->shared(true) → 单例,且支持运行时替换
- 用 $container->set('request', fn() => new Request())->shared(false) → 多例,每次 get 都新造
- Services 类可作为容器的“上游工厂”:比如 Services::logger() 内部调用 $container->get('FileLogger'),把创建和复用解耦
怎么选?看三个实际问题
不用背概念,问自己:
- 这个对象多个地方同时用,会不会互相干扰?→ 干扰就走单例(容器 shared)
- 每次使用都需要干净初始状态?→ 干净就走多例(容器 unshared 或直接工厂调用)
- 以后可能要换实现(比如测试时 mock)?→ 要换就一定走容器绑定,别硬编码 new











