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