The primary uses for DI as a technique are :
It leads to a code that is easier to test ( using Mocking ). This means that in order to use mocking frameworks ( Mockito etc ) , you should be using more of DI. If you dont use DI and write code that instantiates objects directly - then practically you cannot use Mockito to mock your dependencies.
Lets say you write code to play an Orchestra. Your main class depends upon so many other classes ( that other people wrote perhaps ).
Lets say you wrote this :
public class Orchestra {
private Drums drum;
private Violin violin;
private Guitar guitar;
public Orchestra() {
drum = new Drum();
violin = new Violin();
guitar = new Guitar();
}
public Music play(){
// use above in some way to run your orchestra
// start with violin
// add some guitar and then bring in the drums too
}
}
Now you want to ensure that your logic in play
works accurately. When you run your class you see the music is not what you are expecting. ( maybe the drummming is starting right at the start ). You want to test the logic of your code play
. How would you do that here ? You have no control over your dependencies. You dont know if there was an issue with code of Drum
or your own logic in play()
.
You would want to mock out Drum
here. But you cannot do that easily. Because your code does not use DI. Its instantiating the Drum
directly inside.
Now lets use DI and see how it helps.
public class Orchestra {
private Drums drum;
private Violin violin;
private Guitar guitar;
public Orchestra(Drums d,Violin v, Guitar g ) {
drum = d;
violin = v;
guitar = g;
}
public Music play(){
// use above in some way to run your orchestra
}
}
With this code , it becomes easy to mock out Drum
in your test.
class TestOrchestra {
public void testPlay(){
Drum mockDrum = mock(Drum.class);
Violin mockViolin = mock(Violin.class);
Guitar mockGuitar = mock(Guitar.class);
// add mock behaviour to above , here you control precisely what these dependencies will do inside your code
Orchestra orch = new Orchestra(mockDrum, mockViolin,mockGuitar);
// now play and test your logic
}
}
The second advantage of using DI is that it helps vary implementations of bigger parts of your program without needing to go through your entire code.
Again - with reference to above , lets say you have been playing your orchestra using a particular type of guitar ( which is instantiated internally by your code).
Now you want to change to an brand new electric guitar.Without DI , you would have to open up your Orchestra
class , check where you create the Guitar
and change that line of code. You would have to ensure that other parts of your code are not changed inadvertently and test entire Orchestra
to make sure things work correctly.
With DI , you could avoid all this. You just inject the new Guitar
object into your constructor. Thats it. Because you have tested the new Guitar
object on its own ( and it fulfills the contract of the interface Guitar
- you can be rest assured that your Orchestra
code is not broken by injecting this new Guitar. Thats quite a better improving your orchestra.