依赖注入强化封装,通过将依赖来源与使用分离实现真正解耦。它要求类声明接口类型依赖、拒绝内部new实例,并配合容器自动化传入,避免伪封装和测试障碍。

依赖注入让封装更彻底
封装不是简单地把属性设为 private 或加个 get/set 方法,而是要隔离「谁创建依赖」和「谁使用依赖」。依赖注入把外部依赖(比如 SessionStorage、LoggerInterface)从类内部移出去,交由调用方或容器传入——这恰恰强化了类的边界:它只专注自身职责,不操心依赖怎么来、谁管生命周期。
常见错误是:类里直接 new SessionStorage(),看似封装了 session 操作,实则把具体实现和耦合悄悄塞进了内部,违反了「依赖倒置」原则。
- 封装关注「隐藏什么」(数据/实现细节),依赖注入关注「从哪来」(依赖来源)
- 没依赖注入的封装,容易变成「伪封装」:内部仍硬编码依赖,无法替换、难测试
- 配合接口使用时效果最明显,例如声明
private LoggerInterface $logger,而非private FileLogger $logger
构造函数注入是最自然的封装载体
PHP 7.4+ 支持类型化属性,让构造函数注入和封装能严丝合缝地对齐:__construct(LoggerInterface $logger) + private LoggerInterface $logger 组合,既限定了依赖类型,又锁死了访问范围。
这种写法天然阻止了外部绕过构造函数直接赋值,也避免了在方法里临时 new 实例破坏一致性。
立即学习“PHP免费学习笔记(深入)”;
- 不要在构造函数里做复杂逻辑或 I/O,否则封装体本身就成了副作用源头
- 如果依赖可选,用可空类型
?MailerInterface比默认null更清晰 - 避免在构造函数中调用
$this->init()这类方法——它可能依赖尚未注入完成的属性
封装失败常因依赖注入不到位
很多 PHP 类看似用了 private 属性,但构造函数空着、依赖靠全局变量或静态调用(如 Config::get()),结果封装形同虚设:你改不了日志实现,测不了异常路径,换不了缓存驱动。
典型症状包括:
- 单元测试必须启动 session 或连接数据库才能跑通
- 想 mock 一个依赖,发现它藏在
static::getInstance()里出不来 - 类里有
if (ENV === 'test') { ... }分支——说明封装没挡住环境侵入
ThinkPHP 和 CodeIgniter 4 的容器不是替代封装,而是补全它
框架容器(如 app()->make(UserService::class))自动完成依赖解析,但它不改变封装本质。真正起作用的,还是你写的类是否把依赖声明为构造参数、是否用接口类型约束、是否拒绝在内部 new 具体类。
容器只是帮你把「传依赖」这件事自动化了;如果类设计本身没做好封装,容器只会帮你更快地注入一堆紧耦合对象。
最容易被忽略的一点:接口定义本身也是封装的一部分。写 interface CacheInterface { public function get(string $key): mixed; },比直接暴露 Redis 或 FileCache 的方法名,才是对「缓存行为」真正的抽象与封装。



















