Dependency Inversion principle is more about dependencies between modules/projects/packages/libraries than between classes.
Module/Project Domain
public interface IRepository
{
void Save(object data);
}
public class Order
{
private readonly IRepository _repository;
public Order(IRepository repository) => _repository = repository;
public void Save(object data) => _repository.Save(data)
}
Module/Project Database
using Domain;
public class SqlServerRepository : IRepository
{
public void Save(object data)
{
// SqlConnection, SqlCommand, Execute ...
}
}
Application will have only one "main" module - Entry point, which will know about all other modules.
Entry point module will be responsible to build required object graph of your application (instantiate and inject correct implementations)
-------------------------- ------------------------
| **Domain** | | **Database** |
| | <-------------- | |
| IRepository | | |
-------------------------- ------------------------
^ ^
| |
| |
| ----------------------- |
| | **Main** | |
|------------| |-----------|
| |
-----------------------
Entry point
using Domain;
using Database;
public class Program
{
public static void Main()
{
var sqlServerRepository = new SqlServerRepository();
var order = new Order(sqlServerRepository);
Run(order); // Run appliciation
}
}
As you can see, there are no way Domain module could instantiate SqlServerRepository
.
Dependency Inversion principle applied for the modules will prevent Domain module to instantiate Database module instances only because Domain module will not know about Database module existence.
The idea of Dependency Inversion principle is to change dependency direction we have in runtime during design time.
Runtime dependency direction
Order -> SqlServerRepository
Design time dependency direction
SqlServerRepository -> Order
One of the benefit is reduced building time for big projects when you make changes in a low level modules.
For example: I can make change in Database module without need to re-build Domain module.
Dependence inversion inside one module doesn't have much sense, since classes already know about each other and change in one class will require to re-build a module.
You don't need to write against interface inside one module, unless you actually have different implementations for one abstraction.
new TestImpl()
, so neither is making use of dependency inversion (althoughUseTest
itself is). If you want to make sure that consumers useUseTest
instead ofTestImpl
, start by makingUseTest
do something useful.