Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/258.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 让DI容器替换全局$registry对象是否是一种好的做法?_Php_Unit Testing_Dependency Injection - Fatal编程技术网

Php 让DI容器替换全局$registry对象是否是一种好的做法?

Php 让DI容器替换全局$registry对象是否是一种好的做法?,php,unit-testing,dependency-injection,Php,Unit Testing,Dependency Injection,我已经开始重构一个小应用程序,使用一个小的DI容器,而不是 $registry::getstuff();在我的类中,我将调用注入到一个容器中 提出了两个问题, Q1->我扩展并创建了一个容器,其中包含特定于每个需要DI的对象的依赖项。然后,我将对象馈送,并在构造函数中将其解构,将DI的对象分配给我正在构建的对象的类属性 我应该在new object()调用中分离对象吗?我只是觉得这样比较容易,但鉴于我现在是一个人的团队,我只想确认我有合适的方法 Q2->如果我在几个主要类上这样做,我发现我传递的

我已经开始重构一个小应用程序,使用一个小的DI容器,而不是 $registry::getstuff();在我的类中,我将调用注入到一个容器中

提出了两个问题,

Q1->我扩展并创建了一个容器,其中包含特定于每个需要DI的对象的依赖项。然后,我将对象馈送,并在构造函数中将其解构,将DI的对象分配给我正在构建的对象的类属性

我应该在new object()调用中分离对象吗?我只是觉得这样比较容易,但鉴于我现在是一个人的团队,我只想确认我有合适的方法

Q2->如果我在几个主要类上这样做,我发现我传递的$registry对象将不需要,这是使用DI的正常结果吗,不再使用registry?我可能在容器中注入了一个或两个单例,但看起来这就是我所需要的,甚至那些都可以很容易地删除,因为DI有一个share()属性,它返回相同的对象实例,有效地消除了对单例的需要。这是摆脱应用程序需要注册表/单例的方法吗,因为如果是这样,就很容易了。

Q2: 如果您在
$registry
对象上到处传递。。。。然后,您的注册表实际上不是所谓的注册表(正如Fowler所描述的)

注册表或多或少是一个具有get/set方法的全局对象(“众所周知”)。 在PHP中,注册表实现的两个常见原型是

单身

class RegistryAsSingleton
{
    public static function getInstance (){
       //the singleton part
    }

    public function getStuff ()
    {
       //some stuff accessed thanks to the registry
    }
}
到处都是静态方法

class RegistryAsStatic
{
    public static function getStuff()
    {
    }
}
将您的注册表到处传递会使它成为一个对象:一个容器,它的作用并不比提供对其他对象的引用更大

您的DI容器(使用您在OP中建议的Pimple)本身就是一种注册表:它是众所周知的,可以让您从任何地方获取组件

因此,是的,我们可以说您的DI容器将通过执行相同的功能来删除注册表的要求和必要性

但是(总是有一个但是)

登记处总是有罪的,直到 证明无罪 (马丁·福勒)

如果您使用DI容器替换注册表,这可能是错误的

例如:

为什么这么糟糕?因为您的代码的依赖性并不比以前低,所以它仍然依赖于一个没有提供明确目标的组件(注册表或容器)(我们可以在这里考虑接口)

注册表模式在某些情况下很有用,因为它是定义组件或数据(例如全局配置)的简单且相当便宜的方法

通过删除依赖项重构上述示例而不依赖DI的一种方法是:

class WasNeedingARegistry
{
    public function asAParameter (Connection $pConnection)
    {
       $pConnection->doStuff();//The real dependency here, we don't care for 
       //a global registry
    }
}

//the client code would be like
$wasNeedingARegistry = new WasNeedingARegistry();
$wasNeedingARegistry->setConnection($connection);
当然,如果客户机代码不知道连接,这可能是不可能的,这可能是您可能首先结束使用注册表的原因

现在DI开始发挥作用

使用DI使我们的生活变得更好,因为它将处理依赖关系,并使我们能够以随时可用的状态访问依赖关系

在代码的某个地方,您将配置组件:

$container['connection'] = function ($container) {
    return new Connection('configuration');
};
$container['neededARegistry'] = function ($container) {
    $neededARegistry = new NeededARegistry();
    $neededARegistry->setConnection($container['connection']);
    return $neededARegistry;
};
现在,您拥有了重构代码所需的一切:

// probably a better design pattern for using a Registry 
class NeededARegistry
{
    public function setConnection(Connection $pConnection)
    {
       $this->connection = $pConnection;
       return $this;
    }

    public function previouslyAsDirectAccess ()
    {
       $this->connection->doStuff();
    }
}

//and the client code just needs to know about the DI container
$container['neededARegistry']->previouslyAsDirectAccess();
“客户端”代码应尽可能隔离。客户机应该负责并注入自己的依赖项(通过
set-
方法)。客户机不应负责处理其依赖项的依赖项

class WrongClientCode
{
    private $connection;
    public function setConnection(Connection $pConnection)
    {
       $this->connection = $pConnection;
    }

    public function callService ()
    {
       //for the demo we use a factory here
       ServiceFactory::create('SomeId')
                       ->setConnection($this->connection)
                       ->call();
       //here, connection was propagated on the solely 
       // purpose of being passed to the Service
    }
}

class GoodClientCode
{
    private $service;
    public function setService(Service $pService)
    {
       //the only dependency is on Service, no more connection
       $this->service = $pService;
    }

    public function callService ()
    {
       $this->service->setConnection($this->connection)
                     ->call();
    }
}
DI容器将使用已正确配置其连接的服务配置GoodClientCode

至于单身方面,是的,这将使你摆脱他们。
希望这有帮助

(提示)您的Q1是否针对匿名粉刺函数中的对象创建代码?如果是,那么代码示例可能有助于回答这个问题。谢谢你的反馈。你说得很清楚,非常感谢。解释得很好,很透彻。当我的同事不可避免地问我“但我们不能只在容器中传递吗?”时,我肯定会传递一个指向这个答案的链接。他们非常喜欢Zend倾向于接受一系列“配置参数”,在许多情况下应该命名为参数。很好的解释。我喜欢在堆栈上看到坏代码/好代码示例。快速问题:在典型的MVC框架中,DI容器存储在哪里?您是否只有1个定义了所有服务/组件?或者你有很多吗?谢谢你的解释,我现在正在努力理解DI容器,这是一个很大的帮助。这一行在上节课中真的正确吗$this->service->setConnection($this->connection)`为什么GoodClientCode有
这个->连接?
class WrongClientCode
{
    private $connection;
    public function setConnection(Connection $pConnection)
    {
       $this->connection = $pConnection;
    }

    public function callService ()
    {
       //for the demo we use a factory here
       ServiceFactory::create('SomeId')
                       ->setConnection($this->connection)
                       ->call();
       //here, connection was propagated on the solely 
       // purpose of being passed to the Service
    }
}

class GoodClientCode
{
    private $service;
    public function setService(Service $pService)
    {
       //the only dependency is on Service, no more connection
       $this->service = $pService;
    }

    public function callService ()
    {
       $this->service->setConnection($this->connection)
                     ->call();
    }
}