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


Is it good practice to have DI container replace a global $registry object?

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

这引发了两个问题,

Q1->I扩展了Pimple DI类,并创建了一个容器,其中包含特定于每个需要DI的对象的依赖项。然后,我向对象提供整个shebang,并在构造函数中将DI的对象分配给我正在构建的对象的类属性时对其进行去构造。

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

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

Q2:如果你在你的$registry物体上到处走动。。。。那么你的注册表并不是真正的Registry(正如Fowler所描述的)。

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

作为单一

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容器来替换注册表,这可能是错误的。

例如:

//probably a Wrong usage of Registry
class NeedsRegistry
{
    public function asAParameter(Registry $pRegistry)
    {
       //Wrong dependency on registry where dependency is on Connection
       $ct = $pRegistry->getConnection();
    }
    public function asDirectAccess ()
    {
       //same mistake, more obvious as we can't use another component
       $ct = Registry::getInstance()->getConnection();
    }
}
//probably a wrong replacement for Registry using DI Container
class NeedsContainer
{
    public function asAParameter(Container $pRegistry)
    {
       //We are dependent to the container with no needs, 
       //this code should be dependent on Connection
       $ct = $pContainer->getConnection();
    }
    public function asDirectAccess ()
    {
       //should not be dependent on container
       $ct = Container::getInstance()->getConnection();
    }
}

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

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

通过删除依赖项,在不依赖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);

当然,如果客户端代码不知道连接,这可能是不可能的,这可能就是您最初可能使用Registry的原因。

现在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

至于辛格尔顿方面,是的,它会让你摆脱它们。希望这能帮助

相关文章: