在Factory方法中正确使用依赖注入(PHP)

时间:2013-01-22 17:23:22

标签: php dependency-injection inversion-of-control factory-method

当我使用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

OR

$concreteClass = new ConcreteClass; // now you are responsible for setting up dependencies yourself
$concreteClass->inject (new YetAnotherAsphaltClass, 'AsphaltClass');

1 个答案:

答案 0 :(得分:1)

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

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

我建议使用类构造函数作为依赖的接收者,而不是使用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;
    }
}

IRebarIAsphaltISand可以是接口(http://php.net/manual/en/language.oop5.interfaces.php)。

interface IRebar {

}

class MyRebar implements IRebar {
}