使用依赖注入时这很糟糕:
public function __construct($service = null)
{
if(null === $service){
$service = MyNewDefaultService()
}
$this->service = $service;
}
即具有服务的默认后备类类型的概念
使用依赖注入时这很糟糕:
public function __construct($service = null)
{
if(null === $service){
$service = MyNewDefaultService()
}
$this->service = $service;
}
即具有服务的默认后备类类型的概念
这种模式会起作用(事实上,(反)模式有一个名字- Bastard Injection),但是这种方法存在一些问题:
通过在您的消费者类中构建一个新的MyNewDefaultService()
依赖关系,除了抽象之外,您还耦合到一个具体的服务类。在编译语言中,这也意味着您的消费代码现在需要对包含具体依赖类的 jar / library / dll / assembly 进行硬“引用”,而如果您省略直接构造,您可以只在接口上耦合. 在脚本语言中,您需要确保具体的依赖关系在运行时是可解析的。
依赖关系的生命周期管理MyNewDefaultService
现在被硬编码为与消费类的生命周期相同。由 IoC 容器注入和管理的对象的生命周期可以为您提供比这更大的灵活性(例如注入共享对象等)。
测试现在更加复杂,因为您无法模拟“默认”路径(即 when $service == null
),因此您需要混合单元测试(对于注入路径,具有模拟依赖项)和集成测试(对于默认路径)来证明你的代码的正确性。
如果您的依赖项本身还有其他依赖项,这些依赖项也使用构造函数注入,那么默认构造路径很快就会变得笨拙,并导致更多的耦合,因为您现在需要完成所有艰苦的工作来解决您的 IoC 容器为您完成的依赖项,例如
if(null === $service){
$service = MyNewDefaultService(RepoFactory.Create(LoggerFactory.Create()), ...)
}
TL;DR虽然当您从耦合层次结构迁移到由 IoC 容器管理的松散耦合的依赖注入层次结构时,这种方法在过渡阶段可能很有用,但依赖倒置原则的真正好处只有在构造函数只有一条路径,即通过抽象耦合到所有依赖项,而不是任何具体实现。