Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Say I have a class that has a few methods that depend on another object in order to carry out their duty. The difference being they all depend on the same class of object, but need different instances of the class. Or more specifically, each method needs a clean instance of the class because the methods will be modifying the state of the dependency.

Here is a simple example I had in mind.

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

Technically I could do this.

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

My trouble here is I constantly have to check that the dependent object being passed in is in the correct state. The state being newly created. So instead I was thinking of just doing something like this.

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

This allows me to get one instance of an object in the correct state, and I can just copy it if I need another one. I'm just curious if this makes sense, or if I'm over looking a fundamental guideline about dependency injection and keeping code testable.

like image 311
maxiscool Avatar asked Jul 25 '13 01:07

maxiscool


2 Answers

I would use the Factory pattern for this:

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

You can decouple the factory further by introducing an interface:

interface DependencyFactoryInterface
{
    public function create();
}

class DependencyFactory implements DependencyFactoryInterface
{
    // ...
}

class Something {
    public function __construct(DependencyFactoryInterface $factory) 
    ...
like image 132
Ja͢ck Avatar answered Sep 20 '22 05:09

Ja͢ck


DI is a wonderful design pattern, but that doesn't mean it's the right one for any case. In your specific case you said that you need a "fresh" copy every time, which means that simply using "new" to create a new instance makes more sense than using DI.

The idea behind DI is loose coupling - and allowing different places in the code to use the same object (knowing nothing about it other then the interface it implements) - but in your case you don't need to re-use an object, hence I wouldn't use DI here.

like image 30
Nir Alfasi Avatar answered Sep 21 '22 05:09

Nir Alfasi