在工厂方法(PHP)中正确使用依赖项注入

在工厂方法(PHP)中正确使用依赖项注入,php,dependency-injection,inversion-of-control,factory-method,Php,Dependency Injection,Inversion Of Control,Factory Method,当我使用OO设计模式时,我想确保我真的得到了它。我了解依赖注入的重要性,也了解服务容器/工厂对象。我喜欢工厂方法的想法,它可以在通过静态方法加载时将依赖项注入自身,并返回一个完整的自身副本。我喜欢在使用对象的代码中有多么干净。而且,在测试中,您可以插入不同的对象(覆盖或实例化而不使用工厂方法,请参见下文) 以下代码是否会引起警钟?我理解正确吗? abstract class AbstractClass { public function __construct () {

当我使用OO设计模式时,我想确保我真的得到了它。我了解依赖注入的重要性,也了解服务容器/工厂对象。我喜欢工厂方法的想法,它可以在通过静态方法加载时将依赖项注入自身,并返回一个完整的自身副本。我喜欢在使用对象的代码中有多么干净。而且,在测试中,您可以插入不同的对象(覆盖或实例化而不使用工厂方法,请参见下文)

以下代码是否会引起警钟?我理解正确吗?

abstract class AbstractClass
{
    public function __construct ()
    {

    }

    public static function factory ()
    {
        throw new Exception ('Please create a concrete class version of the method ' . __FUNCTION__);
    }

    public function inject ($class, $className=null)
    {

        if ($className === null)
        {
            $className          = get_class ($class);
        }

        $this->{$className} = $class;
    }
}

class ConcreteClass extends AbstractClass
{
    public static function factory ()
    {
        $me = new self;
        $me->inject (RebarClass::factory ());
        $me->inject (AsphaltClass::factory ());
        $me->inject (CementClass::factory ());

        return $me;
    }

    public function doSomething ()
    {
        echo $this->RebarClass->doSomethingCool ();
    }

}

class RebarClass extends AbstractClass
{
    public static function factory ()
    {
        return new self;
    }

    public function doSomethingCool ()
    {
        return "I did something, but it wasn't that cool...\n";
    }
}

class AsphaltClass extends AbstractClass
{
    public static function factory ()
    {
        return new self;
    }
}

class CementClass extends AbstractClass
{
    public static function factory ()
    {
        $me = new self;
        $me->inject (AsphaltClass::factory ());
        $me->inject (SandClass::factory ());

        return $me;
    }
}

class SandClass extends AbstractClass
{
    public static function factory ()
    {
        return new self;
    }
}
对我来说,当我在控制器和其他模型中创建和使用对象时,这给了我很大的灵活性,我可以实例化如下:

$concreteClass = ConcreteClass::factory ();
现在我的目标是按照我想要的方式设置的

print_r ($concreteClass);
echo "\n";
产出:

ConcreteClass Object
(
    [RebarClass] => RebarClass Object
    (
    )

    [AsphaltClass] => AsphaltClass Object
    (
    )

    [CementClass] => CementClass Object
    (
        [AsphaltClass] => AsphaltClass Object
        (
        )

        [SandClass] => SandClass Object
        (
        )

    )

)
在内部,其他对象易于使用

echo $concreteClass->doSomething ();
而且,如果您想将其用于单元测试,您可以执行以下任一操作:

$concreteClass = ConcreteClass::factory ();
$concreteClass->inject(new DifferentAsphaltClass, 'AsphaltClass'); // overwrite


如果不使用工厂,所提出的方法存在一些与正确注入所需类相关的风险

由于所需注入列表不可用,代码也有点难以理解

我建议使用类构造函数作为依赖项的接收者,而不是使用inject方法

class Concrete {
    public static function Factory() {
        $rebar = Rebar::Factory();
        $asphalt = Asphalt::Factory();
        $sand = Sand::Factory();

        return new Concrete($rebar, $asphalt, $sand);
    }

    public function __construct(IRebar $rebar, IAsphalt $asphalt, ISand $sand) {
        $this->Rebar = $rebar;
        $this->Asphalt = $asphalt;
        $this->Sand = $sand;
    }
}
IRebar
IAsphalt
ISand
可以是接口()


我喜欢你的想法,使它更容易找到所需的注射。但是使用构造函数消除了工厂方法的目的(至少,据我所知)。我想要一些东西,使实例化一个对象变得容易,如果我以后需要对它的创建进行更改,它就在一个地方,而不是在整个代码中。同时,我想留下覆盖测试对象的可能性,等等哦!我现在明白了,马丁。只有当我使用“new”时,构造函数才会起作用。美好的还有其他想法吗?否则我就开始喜欢这种处理方法了。@Hans是的,大多数时候你会使用factory方法来构造默认对象。此外,您可以使用“类注册表”之类的工具,根据给定的配置确定要注入哪个类。注意无限循环。@MartinSamson,我一直在使用类注册表,但我喜欢将需求保留在类本身而不是单独的区域中。不过,使用类注册表或工厂方法仍然会给我带来潜在的无限循环问题。
class Concrete {
    public static function Factory() {
        $rebar = Rebar::Factory();
        $asphalt = Asphalt::Factory();
        $sand = Sand::Factory();

        return new Concrete($rebar, $asphalt, $sand);
    }

    public function __construct(IRebar $rebar, IAsphalt $asphalt, ISand $sand) {
        $this->Rebar = $rebar;
        $this->Asphalt = $asphalt;
        $this->Sand = $sand;
    }
}
interface IRebar {

}

class MyRebar implements IRebar {
}