PHP依赖注入 - 疙瘩等. - 为什么使用关联数组与getter?

Dan*_*Dan 17 php dependency-injection

我们正在考虑将依赖注入容器集成到我们的项目中.我看过的每个DIC都使用关联数组和/或魔术方法.例如,这是来自Pimple页面的示例:

$container['session_storage'] = function ($c) {
    return new $c['session_storage_class']($c['cookie_name']);
};

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};
Run Code Online (Sandbox Code Playgroud)

是否有一个原因?我讨厌在我的代码中使用字符串,而不是将在某处显示的文字字符串.你失去了IDE的强大功能(这使代码难以维护,这是我们试图避免的事情!).

我的偏好更像是:

class Container {

    function getSessionStorage()
    {
        return new $this->getSessionStorageClass($this->getCookieName);
    }

    function getSession()
    {
        return new Session($this->getSessionStorage());
    }

}
Run Code Online (Sandbox Code Playgroud)

有没有理由不这样做?如果我们走这条路线,我是否会错过一些不会起作用的疙瘩?

oro*_*kek 10

ArrayAccessPimple扩展的"神奇之处" 在于它完全可重用且可互操作.作为DIC的Pimple的一个重要特征是定义的服务可以利用先前定义的服务和/或参数.让我们说(无论出于何种原因)你有一个Session需要Filter实例的对象.没有DIC你可以写:

$session = new Session(new Filter);
Run Code Online (Sandbox Code Playgroud)

有疙瘩你可以写:

$pimple['filter'] = function($c) {
    return new Filter;
};
$pimple['session'] = function($c) {
    return new Session($c['filter']);
}
Run Code Online (Sandbox Code Playgroud)

Pimple在Session对象的实例化中使用先前注册的"Filter"服务.这种好处并不是实现的DIC所特有的ArrayAccess,但可重用性对于代码重用和共享非常有用.您当然可以为某些服务或所有服务硬编码getter/setter,但可重用性的好处几乎丢失了.

另一种选择是使用魔术方法作为getter/setter.这将使DIC的API更像您在代码中所需的API,甚至可以将它们用作Pimple ArrayAccess代码的包装器(尽管您可能最好在此时编写专用的DIC).包装Pimple的现有方法可能如下所示:

public function __call($method, $args) {
    if("set" === substr($method, 0, 3)) {
        return $this[substr($method, 3)];
    }
    if("get" === substr($method, 0, 3) && isset($args[0])) {
        return $this[substr($method, 3)] = $args[0];
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用__set__get提供对DIC中的服务和参数的类似对象的访问,如下所示:(仍然包含在Pimple的ArrayAccess方法中)

public function __set($key, $value) {
    return $this[$key] = $value;
}

public function __get($key) {
    return $this[$key];
}
Run Code Online (Sandbox Code Playgroud)

除此之外,您可以完全重写DIC以专门使用魔术方法,并且具有类似对象的API语法而不是实现ArrayAccess,但这应该很容易理解:]


Mat*_*oli 5

您关心IDE自动完成,因为您将使用您的容器作为服务定位器,即您将打电话给您的容器.

你不应该理想地这样做.服务定位器模式是一种反模式:您可以从容器中获取它们,而不是注入所需的依赖项(依赖注入).这意味着您的代码已耦合到容器.

疙瘩(以及它的数组访问)并没有真正解决这个问题,所以我不是直接回答你的问题,但我希望它能让它更清晰.


旁注:什么是"理想"的方式?依赖注入.

切勿使用或调用容器,除非在应用程序的根目录下(例如创建控制器).始终注入所需的对象(依赖项),而不是注入整个容器.

  • @Mark 我遗漏了一些但也非常重要的东西:使用容器隐藏了依赖项。您最终会得到不清楚的(可能是无限的)依赖关系,而不是显式的强依赖关系(例如作为构造函数参数),因为您通过容器访问它们。 (2认同)