gpt4 book ai didi

php - PHP 5.4 对象取消引用是否成功缓解了此 DI 容器中静态存储参数的缺点?

转载 作者:可可西里 更新时间:2023-11-01 12:35:47 25 4
gpt4 key购买 nike

公共(public)服务更新:

自从我最初提出这个问题以来,我学到了很多东西。如果您正在阅读本文,请采纳我的建议并完全避免使用 static。只是。不。采用。它。 没有办法进行依赖注入(inject);依赖注入(inject)就是这种方式。


我最近花了很多时间研究各种控制反转 (IOC) 概念。我完全同意那些认为 Service Locator 是一种反模式的人。我构建了一个修补程序,并且对它允许使用静态定位器方法在类中间导入“全局”实体以及隐藏对象的实际依赖项的可能性的能力感到震惊。

从服务定位器开始,我着手创建一个依赖注入(inject) (DI) 容器,它为我提供了静态依赖访问的灵 active ,而没有静态变量的伴随缺点。

下面是此类实现的一个简单示例:

<?php

class Container
{
protected static $params = [];

public function store($key, $val)
{
static::$params[$key] = $val;
return $this;
}

public function fetch($key)
{
if (isset(static::$params[$key])) {
return static::$params[$key];
}
$msg = "No parameter match found in container: $key";
throw new OutOfBoundsException($msg);
}
}

$container = new Container;
$container->store('widgetDep', new WidgetDependency);
$container->store('kumquatDep', new KumquatDependency);

// and somewhere else in the application without access to the global namespace
// (i.e. the $container instance we just created) ...

$widget = new Widget(new Container);
$kumquat = new Kumquat(new Container);

这似乎是朝着正确方向迈出的一步,因为静态 $params 属性受到保护,并且不存在可在“全局”静态范围内访问或操作它的静态方法:对象需要访问访问依赖项的容器。

哦,等等...

不幸的是,在这个容器中存储依赖关系意味着现在每个注入(inject)依赖关系的对象都有一个对容器对象的虚假依赖关系,从而隐藏了它的真实依赖关系。另一个负面影响副作用是每个对象都可以访问容器中每个可用的依赖项,显然,Widget 对象不应该访问 Kumquat 对象的依赖项.此外,通过这种方法使用抽象工厂只会将伪造的依赖项从 WidgetKumquat 类移到工厂中。

PHP 5.4 的替代品

借助 5.4 的新对象构造取消引用功能,我们可以执行以下操作,而无需访问全局命名空间中已创建的 $container 实例:

$widget  = new Widget((new Container)->fetch('widgetDep'));
$kumquat = new Kumquat((new Container)->fetch('kumquatDep'));

使用这种方法我们已经成功:

  1. 消除了 Widget 和 Kumquat 对象的容器依赖性,允许它们的构造函数类型提示它们需要的特定依赖性对象;
  2. 防止下游 Widget 和 Kumquat 对象访问它们不应该知道的依赖项;
  3. 保留静态依赖存储能力。

现在,一个可能的缺点是这种方法意味着开发人员必须遵守足够的纪律,不能将完整的 Container 对象作为依赖项传递。这是至关重要的。

所以问题是...

分两部分:

  1. 您认为这种方法有哪些具体缺点,以及
  2. 静态 Container::$params 是否必要?它是否应该是一个标准的 protected 属性,由全局命名空间中的顶级对象图工厂类/方法访问(避免需要 static)?

最佳答案

你根本不应该在这里使用static。只需创建一个容器:$container = new DIContainer(); 并将该对象用作典型的依赖项。毕竟在应用程序的核心中只有极少数地方需要访问整个容器。

看看Symfony's Dependency Injection component - 一段相当不错的代码。


编辑:

根据第一条评论。是的,你误会我了。通常你只需要容器中的几个依赖项,所以你会这样写:

$service = new Service($container->get('dep.a'), $container->get('dep.b'), 123);

我的观点是你不应该在容器中使用静态属性,因为它只是一个全局对象。之间没有区别:

global $container;
$widget = new Widget($container->fetch('widgetDep'));
$kumquat = new Kumquat($container->fetch('kumquatDep'));

$widget = new Widget(Container::getInstance()->fetch('widgetDep'));
$kumquat = new Kumquat(Container::getInstance()->fetch('kumquatDep'));

// You're using new objects but they share the same, **global** array.
// Therefore, they are actually global themselves.
$widget = new Widget((new Container())->fetch('widgetDep'));
$kumquat = new Kumquat((new Container())->fetch('kumquatDep'));

换句话说,容器本身应该是一个局部变量,如果您需要在其他地方访问它(某些对象可能需要访问整个容器),那么您应该明确地将它作为该对象的依赖项传递。

正如我之前所说,看看 Symfony DIC 和整个框架,看看如何制作一个好的、编写良好的 DIC。


简单容器:

class Container {
private $services = array();

public function get($service) {
if (!array_key_exists($this->services, $service)) {
throw ...;
}

return $this->services[$service];
}
}

$containerA = new Container();
$containerB = new Container();

// $containerA and $containerB are completely different
// objects and don't share anything

关于php - PHP 5.4 对象取消引用是否成功缓解了此 DI 容器中静态存储参数的缺点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9179586/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com