Dependency Inversion Principle
The Dependency Inversion Principle states that:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
Dependency inversion principle is one on which most of the design patterns are build upon. It's aim is to reduce the coupling between the classes is achieved by introducing abstraction between the layer, thus doesn’t care about the real implementation.
Dependency Inversion Principle Example
Let’s understand with an example: we have a manager having some persons as an employee of which some are team leads, developers and testers.
Violating Dependency Inversion Principle
Let’s see how a design would look without any dependency inversion and what are the drawbacks in that design.
class Program { static void Main(string[] args) { Manager manager = new Manager(); manager.addDeveloper(new Developer()); manager.addTeamLead(new TeamLead()); manager.addTester(new Tester()); } } class Manager { List<TeamLead> teamLeads = new List<TeamLead>(); List<Developer> developer = new List<Developer>(); List<Tester> tester = new List<Tester>(); public void addTeamLead(TeamLead _teamLead) { teamLeads.Add(_teamLead); } public void addDeveloper(Developer _developer) { developer.Add(_developer); } public void addTester(Tester _tester) { tester.Add(_tester); } } class TeamLead { //some code } class Developer { //some code } class Tester { //some code }
First, we have exposed everything about the lower layer to the upper layer, thus abstraction is not mentioned. That means Manager must already know about the type of the workers that he can supervise.
Now if another type of worker comes under the manager lets say, Consultant, then the whole class needs to be rejigged. This is where dependency inversion principle comes.
Dependency Inversion Principle Example
Let’s see how to solve the problem using Dependency Inversion Principle.
class Program { static void Main(string[] args) { Manager manager = new Manager(); manager.addEmployee(new Developer()); manager.addEmployee(new TeamLead()); manager.addEmployee(new Tester()); } } class Manager { List<Employee> employee = new List<Employee>(); public void addEmployee(Employee _Employee) { employee.Add(_Employee); } } abstract class Employee { //some code } class TeamLead : Employee { //some code } class Developer : Employee { //some code } class Tester : Employee { //some code }
In this scenario the manager doesn't have an idea beforehand about all the type of workers that may come under him/her making the code truly decoupled.