Any application is composed of many objects that collaborate with each other to perform some useful stuff. Traditionally each object
is responsible for obtaining its own references to the dependent objects (dependencies) it collaborate with. This leads to highly coupled classes
and hard-to-test code.
For example, consider a Car object. A Car depends on Wheels, Engine, Fuel, Battery, etc to run. Traditionally we define the brand of
such dependent objects along with the definition of the Car object.
private Wheel wh= new NepaliRubberWheel();
private Battery bt= new ExcideBattery();
Here, the Car object is responsible for creating the dependent objects.
What if we want to change the type of its dependent object - say Wheel - after the initial NepaliRubberWheel() punctures? We need to
recreate the Car object with its new dependency say ChineseRubberWheel(), but only the Car manufacturer can do that.
Then what the Dependency Injection do us for ...
When using Dependency Injection, objects are given their dependencies at run time rather than compile time (car manufacturing time).
So that we can now change the Wheel whenever we want. Here, the Dependency (Wheel) can be injected into Car at run time.
Inversion of Control (IoC) is a general concept, and it can be expressed in many different ways and Dependency Injection is merely one concrete example
of Inversion of Control.
This concept says that you do not create your objects but describe how they should be created. You don't directly connect your
components and services together in code but describe which services are needed by which components in a configuration file. A container
is then responsible for hooking it all up.
Reference: Understanding Dependency Injection