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

2022-08-30 22:20:57

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

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

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

这是有原因的吗?我讨厌在我的代码中使用字符串,而不是将要在某个地方显示的文字字符串。你失去了IDE的强大功能(这使得代码更难维护,这是我们试图避免的!

我更喜欢这样:

class Container {

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

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

}

有理由不这样做吗?我是否错过了一些疙瘩的魔力,如果我们走这条路,这些魔力将不起作用?


答案 1

Pimple中扩展的“魔力”在于它是完全可重用和可互操作的。作为DIC,Pimple的一大特点是定义的服务可以利用以前定义的服务和/或参数。假设(无论出于何种原因)您有一个需要实例的对象。如果没有DIC,你可以写:ArrayAccessSessionFilter

$session = new Session(new Filter);

用疙瘩,你可以写:

$pimple['filter'] = function($c) {
    return new Filter;
};
$pimple['session'] = function($c) {
    return new Session($c['filter']);
}

Pimple 在 Session 对象的实例化中使用以前注册的“过滤器”服务。这个好处并不是实现的DIC所独有的,但可重用性对于代码重用和共享非常有用。您当然可以为某些服务或所有服务对 getter/setter 进行硬编码,但可重用性的好处几乎已经丢失。ArrayAccess

另一种选择是使用魔术方法作为 getter/setter。这将为DIC提供一个更像你在代码中想要的API,你甚至可以将它们用作Pimple代码的包装器(尽管在这一点上编写一个专用的DIC可能会更好)。包装在 Pimple 的现有方法上可能如下所示:ArrayAccess

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;
}

您还可以使用 和 授予对 DIC 中的服务和参数的类似对象的访问,如下所示:(仍在包装 Pimple 的方法)__set__getArrayAccess

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

public function __get($key) {
    return $this[$key];
}

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


答案 2

您关心 IDE 自动完成,因为您将容器用作服务定位符,即要调用容器。

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

Pimple(及其阵列访问)并不能真正解决这个问题,所以我不直接回答你的问题,但我希望它能使它更清晰。


附注:什么是“理想”方式?依赖注入。

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


推荐