History
In our previous posts, we learned ‘What is S.O.L.I.D. Programming Principles’ and a detailed explanation with code of Single Responsibility Principle.
S.O.L.I.D. is an acronym introduced by Michael Feathers as:
- S for SRP: Single responsibility principle
- O for OCP: Open/closed principle
- L for LSP: Liskov substitution principle
- I for ISP: Interface segregation principle
- D for DIP: Dependency inversion principle
Single Responsibility Principle says, class should have single responsibility. In reference to this, I would say “A class should have single responsibility”.
Let's dive into the ocean – can we read this like “a class should not design to do multiple activities”.
This is a very vast topic and it is not possible to learn/explain it in a one-shot. I divided this into the following parts:
Introduction
In this whole article, we will learn Open Closed Principle in details with example.
First of all, let's revise what is OCP [ref. to Learning The S.O.L.I.D Programming Principles: Overview [Part - I]]?
A definition from wiki:
Quote:
software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
Learning Open/Closed Principle (OCP)
I took many weeks to understand this principle and when I visited my old code, I was shocked to see that I violate OCP in many of my previously written codes.
Let's explore this with an example: we need to update our database from one server to another server (suppose we need to refresh our development database from production), but there are some rules like data-type should be the same, data value should be changed, etc.
public class ValidateData
{
public void SyncronizeData(ServerData data, SourceServerData sourceData)
{
if (IsValid(data, sourceData))
{
}
}
private bool IsValid(ServerData data, SourceServerData sourceData)
{
var result = false;
if (data.Type == sourceData.Type)
result = true;
if (data.IP != sourceData.IP)
result = true;
return result;
}
}
Wait here to think about what is wrong with the above code…
Any guesses…
Ok, let's discuss now, take a look into class ValidateData
. F
irst of all, it is doing two activities, in other words, our class is responsible for two things:
- to validate incoming data (from source server)
- to save data
Now, think of a scenario, if someone wants to extend this so, it could use another external service. In this scenario, he/she would have no other choice and have to modify IsValid
method. Also, if someone needs to make it as a component and provide to third parties for their use, then its users would have no way to add another service. This means this class is not open for extensions. On the other hand, if someone needs to modify the behavior to persist data, she/he needs to change the actual class.
To sum up, this class is directly violating OCP as this is neither open for extensions not closed for modifications.
So, what would be a better solution for this class so it should follow, OCP.
Remember abstraction
, let's try to do something by creating an interface:
public interface IDataValidator
{
bool Validate(ServerData data, SourceServerData sourceData);
}
Here, I created an interface IDataValidator
which is having only one method Validate
. Method name describes itself, it's a part of DataValidator
so, it should validate data so, it named as Validate
.
Now, create validators of type IDataValidator
. Something like:
public class IPValidator : IDataValidator
{
public bool Validate(ServerData data, SourceServerData sourceData)
{
return data.IP != sourceData.IP;
}
}
public class TypeValidator : IDataValidator
{
public bool Validate(ServerData data, SourceServerData sourceData)
{
return data.Type == sourceData.Type;
}
}
Forget redesign our class ValidateData
as:
public class ValidateData
{
public bool IsDataValidated(ServerData data, SourceServerData sourceData)
{
IList<IDataValidator> validators = new List<IDataValidator>();
validators.Add(new IPValidator());
validators.Add(new TypeValidator());
return IsDataValid(validators, data, sourceData);
}
private bool IsDataValid(IList<IDataValidator> validators,
ServerData data, SourceServerData sourceData)
{
foreach (var validator in validators)
{
if (validator.Validate(data, sourceData))
return true;
}
return false;
}
}
Stay here to discuss the above snippet! In the above, we have a ValidateData
class, which is only responsible for validating data by certain validations/rules.
With the above changes, our class is not more stable and robust, we can add many validators as we want. Also, you can use this validator to save your data.
Ah! I forget to mention, you can save the data just by calling this validator to another class, it could be repository class or your custom class where you just persist your data.
I am not going to write that part of save, you can easily implement this by yourself. :)
Revisiting – Learning The S.O.L.I.D Programming Principles: Open Closed Principle [Part - III]
Open Closed Principle says “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”
How to Download the Source Code?
You can download complete souce code of examples used in this article from GitHub: Learning Solid.