Say you have a class named AbcClass.
If AbcClass depends on AnotherClass, and AnotherClass eventually needs to make a network call (e.g. to S3, Databse, GIS, some other party), then don't instantiate AnotherClass inside AbcClass.
AnotherClass should be injected into the constructor of AbcClass.
Say AnotherClass is instantiated inside AbcClass.
When you write a test for AbcClass, AnotherClass will inevitably make a network call.
Network calls are unreliable and unpredictable. You have less control over it.
When you're unit testing AbcClass, you want to test it in isolation and not have to depend on a network call.
You could mock AnotherClass that is inside AbcClass, but it's harder to mock it.
It's better to inject AnotherClassMock into the constructor of AbcClass so that u have complete control over the behavior of AbcClass.
This makes AbcClass less coupled to AnotherClass.
Loose coupling + high cohesion is one of the hallmarks in software development. Loose coupling / high cohesion makes your program:
- easy to extend
- easy to maintain and debug
- easy to read and reason about
Pieces of code that belong together should stay together. For example, you take the train and hop in to carriage 1.
- You put your handbag in carriage 2.
- You put your luggage in carriage 3.
- You put your backpack in carriage 4.
It doesn't make sense. You, your handbag, your luggage and your backpack should stay with you on carriage 1.
https://en.wikipedia.org/wiki/Loose_coupling
https://en.wikipedia.org/wiki/Cohesion_(computer_science)
What's better than dependency injection? Dependency inversion of course. See https://khalilstemmler.com/articles/tutorials/dependency-injection-inversion-explained