Dependency injection is one of my favorite design patterns to develop highly-testable and modular code. To apply this pattern, all you have to do is follow two simple guidelines:
- Separate object construction from usage. In practical terms: stop creating objects inside constructors and take those objects as input arguments.
- Use interfaces instead of concrete types as constructor parameters. In this way, the receiver remains agnostic to the implementation of those types and thus it becomes possible to supply different implementations.
Source: Rust traits and dependency injection, an article by Julio Merino.