A dependency can be thought of as anything your code depends on in order to work.
As an example, say we have a class like below:
import { dependency } from 'dependency-library';
class ExampleClass = {
public doSomething() {
dependency.doSomething();
}
}
We can say that ExampleClass
depends on dependency-library
, or dependency-library
is a dependency of ExampleClass
.
In this example we could say that ExampleClass
is tightly coupled to dependency-library
.
Any time we create a new ExampleClass()
, we will also directly pull in dependency-library
.
If for some reason we don’t want these two to be directly coupled, we could use dependency injection:
Dependency injection version:
class ExampleClass = {
constructor(private dependency: DependencyLibrary) {}
public doSomething() {
this.dependency.doSomething();
}
}
const instantiatedExampleClass =
new ExampleClass(new DependencyLibrary());
Why bother?
- Generally decoupled code is easier to modify and less fragile.
- It makes unit testing very simple as spying on injected dependencies and passing in simpler implementations is easy.
- It can allow for highly configurable code where different implementations that use the same public interface can be subbed in where needed.