Dependency Inversion Principle [DIP]
High level modules (e.g. business logic) should not depends on low level modules (e.g. infrastructure). Both should depend on ABSTRACTIONS
DETAILS should depend on ABSTRACTIONS.
ABSTRACTIONS should not depend on DETAILS.
Dependencies
references required to compile or run (e.g. reflection)
Abstractions
Describe 'what needs to be done' (but not how)
- interfaces
- abstract base classes
- types that cannot be instantiated (broadly speaking)
Details
Describe the 'how' of what needs to be done
Can also include:
- details as parameters
- details as return types
Examples of low level dependencies
- databases
- files system
- configuration
- Web APIs
- system clock
Hidden Direct Dependencies
static
calls and new
keyword, could indicate a direct dependency. These can create tight coupling which can make it difficult to isolate and unit test and can cause duplicated code.
Using new
- 'new is glue'
- it creates coupling
- do you need to specify an implementation?
- could you use an abstraction instead?
Explicit Dependency Principle
Your classes shouldn't have hidden dependencies in them. List your classes dependencies upfront, in the constructor. Think of them like the ingredients in a recipe - it's best if you know exactly what you need, before you start!
Dependency Injection [DI]
You don't create your own dependencies, it's the client's job to inject them into your class. (and naturally your class should declare its dependencies as abstractions)
Dependencies can be injected as:-
- constructor arguments (preferred method)
- properties
- method arguments
By using constructor arguments it's possible to leverage IOC (Inversion of Control) containers to construct types and their dependencies.
See also the Strategy Design Pattern