【发布时间】:2016-04-07 21:51:07
【问题描述】:
我是 DI 模式的新手,因此,这是我的问题。
4 月 11 日编辑。
如果我有某个工厂实现了一些业务逻辑,并且在运行时创建了它的主要对象和它的依赖项,这取决于给定的输入参数,我应该如何实现它以便它不会违反 DI 原则? (应该避免直接调用 new 操作符来实例化它的主要对象(由这个工厂产生)和它的依赖关系,不是吗?)
(4 月 11 日编辑结束)
考虑以下 CarFactory 示例:
interface IEngine {}
class RegularEngine implements IEngine {}
class AdvancedEngine implements IEngine {}
class Car
{
private $engine;
public function __construct(IEngine $engine)
{
$this->engine = $engine;
}
}
class CarFactory
{
public function __invoke(bool $isForVipCustomer = false): Car
{
// @todo Here, CarFactory creates different Enginges and a Car,
// but they should be injected instead?
$engine = $isForVipCustomer ? new AdvancedEngine : new RegularEngine;
return new Car($engine);
}
}
// And here is my composition root:
$carFactory = new CarFactory;
$carForTypicalCustomer = $carFactory();
$carForVipCustomer = $carFactory(true);
现在,如您所见,我的工厂实现打破了 DI 原则:它不是从调用代码(如果我使用 DI 容器,来自 Injector)接收实例,而是自己创建它们。所以,我试图重写它,使它不违反 DI 原则。这是我得到的:
interface ICarSimpleFactoryForCarFactory
{
public function __invoke(IEngine $engine): Car;
}
interface IEngineSimpleFactoryForCarFactory
{
public function __invoke(): IEngine;
}
class CarFactory
{
private $carSimpleFactory;
private $regularEngineSimpleFactory;
private $advancedEngineSimpleFactory;
public function __construct(
ICarSimpleFactoryForCarFactory $carSimpleFactory,
IEngineSimpleFactoryForCarFactory $regularEngineSimpleFactory,
IEngineSimpleFactoryForCarFactory $advancedEngineSimpleFactory
)
{
$this->carSimpleFactory = $carSimpleFactory;
$this->regularEngineSimpleFactory = $regularEngineSimpleFactory;
$this->advancedEngineSimpleFactory = $advancedEngineSimpleFactory;
}
public function __invoke(bool $isForVipCustomer = false): Car
{
$engine = ($isForVipCustomer ? $this->advancedEngineSimpleFactory :
$this->regularEngineSimpleFactory)();
return ($this->carSimpleFactory)($engine);
}
}
// And here is my composition root:
// (sinse I'm now using DI, I need to inject some dependencies here)
$carFactory = new CarFactory(
new class implements ICarSimpleFactoryForCarFactory {
public function __invoke(IEngine $engine): Car
{
return new Car($engine);
}
},
new class implements IEngineSimpleFactoryForCarFactory {
public function __invoke(): IEngine
{
return new RegularEngine;
}
},
new class implements IEngineSimpleFactoryForCarFactory {
public function __invoke(): IEngine
{
return new AdvancedEngine;
}
}
);
$carForTypicalCustomer = $carFactory();
$carForVipCustomer = $carFactory(true);
我不得不介绍 2 个小型工厂的新接口,它们的唯一职责是让 Dependency Injector 有机会向它们注入实例,并分别向主 CarFactory 注入实例。 (如果我使用 DI 容器。)
在我看来,这样的解决方案过于复杂并且违反了 KISS 设计原则:只是为了将“new”运算符的使用从我的类转移到组合根,我必须创建一个接口及其实现,只是为了让注射器做它的工作?
那么,遵循 DI 模式实现工厂的正确方法是什么? 或者,我提供的解决方案是这个问题的唯一正确答案?
【问题讨论】:
标签: php design-patterns dependency-injection factory