I\'ve done plenty of reading on Dependency Injection, but I have no idea, how does it actually reduce coupling?
The analogy I have of DI is that all components are r
Dependency Injection (DI) doesn't reduce coupling, per se, because the component that relies on the dependency is still coupled to its dependency. What DI does accomplish, however, is to remove the responsibility of finding the dependency from the component itself, and placing that responsibility elsewhere.
A component that relies on DI is completely passive when it comes to its dependencies. There is no code in the component that says "create a new instance of this dependency" or "go out and get me this dependency". The dependency is given to (injected into) the component, usually when the component itself is created by some other object.
This reversal of responsibility to create (or ask for creation of) a dependency is called Inversion of Control (IoC).
So, if the component doesn't know how to create or ask for a dependency, where does that responsibility lie? Usually in an object created specifically for dependency resolution, commonly called an IoC container. In your analogy, this is your "treasure chest". The IoC container contains the instructions that basically say "when somebody asks for this, give them one of these. An IoC container can typically inspect the component it's asked to create, figure out what its dependencies are, and create them too, walking down the "dependency chain" until all of the dependencies are resolved.
The big shift in thinking, the injection, comes when deciding *who gets to ask the container for the component's dependency"? Without DI, it would be the component itself that would ask the container for its dependency. Using DI, however, the responsibility of asking the container to "resolve" a component's dependency falls to whatever creates or uses the component. When the component is created, whatever is creating it has the responsibility of providing all of the dependencies. The component doesn't know or care how they are created, just that they are.
Now, if the dependency is defined as a concrete implementation, the component is still tightly coupled to that specific concrete implementation, even though it is being injected. DI itself does not reduce coupling in that sense. But if the dependency is defined as an interface, the component doesn't care or know what the concrete implementation is, nor how it's created. It's still coupled to the dependency, but it is a very loose coupling.
In that sense, "Dependency Injection" and "Programming to Interfaces" combine to create very loosely coupled, highly flexible components.