Introduction
Here, I am going to discuss the Open closed and Dependency inversion principle of S<code>O
LID . Now what does SOLID mean? SOLID is an object oriented design principle, where each letter has it own meaning.
- S-> Single responsibility
- O-> Open Closed
- L-> Liskov substitution
- I-> Interface segregation
- D-> Dependency inversion
According to Wikipedia, the definition of SOLID is:
"SOLID are guidelines that can be applied while working on software to remove code smells by causing the programmer to refactor the software's source code until it is both legible and extensible."
Using the Code
Before start with the technical discussion, I would like to answer the below questions:
What is Open closed principle ?
Answer: "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"
What is Dependency inversion?
Answer:
- High-level modules should not depend on low-level modules. Both should depend on abstraction.
- Abstractions should not depend upon details. Details should depend upon abstractions.
Let's consider an example to make it better understood. Suppose I need a computer, not decided yet whether I need a laptop, desktop, tablet or other. Suppose I went to a Computer Shop and ask for a desktop computer. Let's convert my requirements into code.
public class Desktop
{
public string GetComputerDescription()
{
return " You get a Desktop";
}
}
public class Laptop
{
public string GetComputerDescription()
{
return " You get a Laptop";
}
}
public class ComputerShop
{
public enum ComputerType
{
Laptop, Desktop
}
public ComputerShop()
{
}
public string GetMyComputerDescription(ComputerType cmptype)
{
var myComp = string.Empty;
if (ComputerType.Desktop == cmptype)
{
myComp = new Desktop().GetComputerDescription();
}
else if (ComputerType.Laptop == cmptype)
{
myComp = new Laptop().GetComputerDescription();
}
return myComp;
}
}
So, I ask for a desktop computer, right?
var computerShop = new ComputerShop();
computerShop.GetMyComputerDescription(ComputerShop.ComputerType.Desktop);
If you run the code, it will execute fine and give you an output "You get a Desktop". So what's wrong? Here, we are violating the rule of OCP (Open Closed principle) and DI (Dependency inversion).
- Closed for modification (we did violation of OCP)
- High-level modules should not depend on low-level modules. Both should depend on abstraction. (we did violation of DI)
Still confused? No problem, I am explaining.
How do we violate the modification rule?
Yes, we did it by creating the object of Desktop
and Laptop
Class on GetMyComputerDescription
method. Because if a new type comes, then we need to modify the function as well as class enam.
How do we violate the DI Principle?
Yes, we did it by creating the low level object (Desktop
and Laptop
) on high level object (ComputerShop
). So High level module (ComputerShop
) depends on low level module (Laptop
, desktop
) and no abstraction here.
Now, I am adding a new type (Tablet
) and modify my class by violation of the OCP. Look at the violated code below:
public class ComputerShop
{
public enum ComputerType
{
Laptop, Desktop, Tablet
}
public ComputerShop()
{
}
public string GetMyComputerDescription(ComputerType cmptype)
{
var myComp = string.Empty;
if (ComputerType.Desktop == cmptype)
{
myComp = new Desktop().GetComputer();
}
else if (ComputerType.Laptop == cmptype)
{
myComp = new Laptop().GetComputer();
}
else if (ComputerType.Tablet == cmptype)
{
myComp = new Tablet().GetComputer();
}
return myComp;
}
}
Did you notice the violation? Yes, we modify the class as new type introduced. Now let's follow the OCP & DI rule and implement a new structure. If we can get the required computer without creating the objects on shop class, then we can achieve our goal. Now we are going to introduce the abstraction.
public interface IComputer
{
string GetComputerDescrption();
}
public class Desktop : IComputer
{
public string GetComputerDescrption()
{
return " You get a Desktop";
}
}
public class Laptop:IComputer
{
public string GetComputerDescrption()
{
return " You get a Laptop";
}
}
public class Tablet : IComputer
{
public string GetComputerDescrption()
{
return " You get a new Tablet yahooooooooooooooooo";
}
}
public class ComputerShop
{
public string GetMyComputerDescription(IComputer cmptype)
{
var myComp = cmptype.GetComputerDescrption();
return myComp;
}
}
var computer = new ComputerShop();
computer.GetMyComputerDescription(new Tablet());
We introduce an Interface (IComputer
) to remove the dependency from ComputerShop
, Desktop
and Laptop
Class. Also, now both high level and low level module depends on abstraction, not on each other and the abstraction does not depend on details, so if the details change, they should not affect the abstraction (satisfy DI). Now we extend our new item (Tablet
) without modifying the ComputerShop
class (satisfy OCP).
Points of Interest
So the interface gives an extendability and removes dependency. Now whatever computer type comes, Shop
class does not need to depend on any lower level module.
public class AnotherNewItem: IComputer
{
public string GetComputer()
{
return " You get a Another New Item Hurrayyyyyyy";
}
}
It simply gives the product we need from abstraction:
public class Shop
{
public string GetMyComputer(IComputer cmptype)
{
var myComp = cmptype.GetComputer();
return myComp;
}
}
So now our code satisfies both DI and OCP.