【问题标题】:Does it make sense to clone copies of dependencies (Dependency Injection)?克隆依赖项的副本是否有意义(依赖注入)?
【发布时间】:2013-07-25 01:58:30
【问题描述】:

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

这是我想到的一个简单示例。

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

这使我能够获得一个处于正确状态的对象实例,如果我需要另一个实例,我可以复制它。我只是很好奇这是否有意义,或者我是否过度关注有关依赖注入和保持代码可测试的基本准则。

【问题讨论】:

    标签: php oop dependency-injection


    【解决方案1】:

    我会为此使用工厂模式:

    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) 
        ...
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 有道理,但是在单元测试时如何模拟我的依赖对象?
      • 你是指嘲讽Something还是嘲讽Dep
      • 在测试某些东西时我需要模拟 Dep。
      • 以这种方式,使用继承自Dep 并覆盖所需内容的DepDepMock 并不重要
      • 但是如果我忽略 DI,就像你说的那样,只是用“new Dependency()”创建一个新对象,我怎么能用模拟对象交换它呢?除非,我没有完全理解你的意思。
      【解决方案3】:

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

      但是如果两者都负责需要克隆依赖对象的两个不同的动作,那就不错了。

      【讨论】:

        猜你喜欢
        • 2018-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-14
        • 2020-04-14
        • 1970-01-01
        • 2020-12-20
        • 1970-01-01
        • 2013-04-30
        相关资源
        最近更新 更多