What is DI ?
DI (Dependency Injection) is a general technique in OOP that is all about removing unconcerned dependencies from your code. It brings testability, maintainability, extensibility, and exchangeability to your object graph.
In all programming paradigms, the basic design goal is loose coupling and strong cohesion. OOP (Object Oriented Programming) achieves this through objects:
- Objects hide the details of their responsibility (encapsulation).
- Objects transfer work outside their responsibilities to other objects.
However, there is a fundamental problem with doing this. If you write the delegation object in the class code, it means tight coupling at the source code level. The only way to exclude an unconcerned dependency from a class is to pass it in from outside.
Thus, if your class receives dependencies externally, it requires assistance from the outside. DI is a technique that provides a mechanism to resolve dependencies completely externally.
Further reading:
Terminology
- DI Container: Manages dependent references and executes auto-wiring.
- Composition Root: The place where you configure dependency resolution.
- Auto-wiring: A feature that allows you to manage services in the container with minimal configuration. DI libraries usually handle this.
- IoC (Inversion of Control): Making the object with control flow responsibility the entry point. In simple and traditional programming, the entry point is where the responsibility for interpreting user input lies.
Why DI for Unity ?
In Unity, MonoBehaviour is the entry point for our C# code. On the other hand, MonoBehaviour is also a "View component".
In modern application design, "separation of domain logic and presentation layer (View component)" is important.
Ideally, MonoBehaviour should not have so many roles (event handling, control flow, domain logic calls, etc.) in addition to its behavior as a View.
One of the purposes of DI is IoC (Inversion of Control). With DI containers, we can make pure C# classes the entry point (instead of MonoBehaviour). This means that the control flow and other domain logic can be separated from the function of MonoBehaviour as a view component.
View components are dynamically created and destroyed at runtime, while "features" such as control flow and domain logic have a more stable lifespan.
Generally speaking, it's a good idea to make the View layer stateless and separate it from control flow and data management.
This is the main reason I like DI.