克隆依赖项的副本(依赖项注入)有意义吗


Does it make sense to clone copies of dependencies (Dependency Injection)?

假设我有一个类,它有一些方法依赖于另一个对象来执行它们的职责。不同的是,它们都依赖于同一类对象,但需要不同的类实例。或者更具体地说,每个方法都需要一个干净的类实例,因为这些方法将修改依赖关系的状态。

这是我脑海中的一个简单的例子。

class Dependency {
    public $Property;
}

class Something {
    public function doSomething() {
        // Do stuff
        $dep = new Dependency();
        $dep->Property = 'blah';
    }
    public function doSomethingElse() {
       // Do different stuff
       $dep = new Dependency(); 
       $dep->Property = 'blah blah';
    }
}

从技术上讲,我可以做到。

class Something {
    public function doSomething(Dependency $dep = null) {
        $dep = $dep && is_null($dep->Property) ? $dep : new Dependency();
        $dep->Property = 'blah';
    }
    public function doSomethingElse(Dependency $dep = null) {
       $dep = $dep && is_null($dep->Property) ? $dep : new Dependency();
       $dep->Property = 'blah blah';
    }
}

我的问题是,我必须不断地检查传入的依赖对象是否处于正确的状态。正在新建的状态。所以我反而想做这样的事情。

class Something {
    protected $DepObj;
    public function __construct(Dependency $dep) {
        $this->DepObj = $dep && is_null($dep->Property) ? $dep : new Dependency();
    }
    public function doSomething() {
        // Do stuff
        $dep = clone $this->DepObj;
        $dep->Property = 'blah';
    }
    public function doSomethingElse() {
       // Do different stuff
       $dep = clone $this->DepObj;
       $dep->Property = 'blah blah';
    }
}

这允许我获得处于正确状态的对象的一个实例,如果我需要另一个实例的话,我可以直接复制它。我只是想知道这是否有意义,或者我是否过度寻找了关于依赖注入和保持代码可测试性的基本准则。

我会使用工厂模式:

class Dependency {
    public $Property;
}
class DependencyFactory
{
    public function create() { return new Dependency; }
}
class Something {
    protected $dependencies;
    public function __construct(DependencyFactory $factory) {
        $this->dependencies = $factory;
    }
    public function doSomething() {
       // Do different stuff
       $dep = $this->dependencies->create();
       $dep->Property = 'Blah';
    }
    public function doSomethingElse() {
       // Do different stuff
       $dep = $this->dependencies->create();
       $dep->Property = 'blah blah';
    }
}

您可以通过引入一个接口来进一步解耦工厂:

interface DependencyFactoryInterface
{
    public function create();
}
class DependencyFactory implements DependencyFactoryInterface
{
    // ...
}
class Something {
    public function __construct(DependencyFactoryInterface $factory) 
    ...

DI是一种出色的设计模式,但这并不意味着它适合任何情况。在您的具体案例中,您说过每次都需要一个"新"副本,这意味着简单地使用"新"创建一个新实例比使用DI更有意义。

DI背后的思想是松散耦合,允许代码中的不同位置使用同一个对象(除了它实现的接口之外,对它一无所知),但在您的情况下,您不需要重用对象,因此我在这里不使用DI。

如果doSomething()doSomethingElse()在两个函数中都做类似的事情,那么这是一个糟糕的选择。在这种情况下,它创建了一个馈送器函数,并向该函数馈送两个不同的对象。

但是,如果两者都负责两个不同的操作,需要克隆依赖对象,那么这还不错。