Learning The S.O.L.I.D Programming Principles: Open Closed Principle [Part – III]

History:

In our previous posts we learned ‘What is S.O.L.I.D. Programing Principles’ and a detailed explanation with code of Single Responsibility Principle.


S.O.L.I.D. is an acronym introduced by Michael Feathers as:

  1. S for SRP: Single responsibility principle
  2. O for OCP: Open/closed principle
  3. L for LSP: Liskov substitution principle
  4. I for ISP: Interface segregation principle
  5. 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”.

Lets dive into ocean – can we read this like “a class should not design to do multiple activities”.

This is a very vast topic and this is not possible to learn/explain in a one-shot. I divided this into following parts:

Introduction

In this whole article, we will learn Open Closed Principle in details with example.

First of all lets revise what is OCP [ref. to Learning The S.O.L.I.D Programming Principles: Overview [Part – I]]?
A definition from wiki:
“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 shocked to see that I violate OCP in many of my emviously written codes.

Lets explore this with an example:we need to update our database from one server to another server (suppose need to refresh our Development database from production), but there are
some rules like data-type should be same, data value should be changed etc.

	public class ValidateData
    {
        public void SyncronizeData(ServerData data, SourceServerData sourceData)
        {
            if (IsValid(data, sourceData))
            {
                //save data
            }
        }

        private bool IsValid(ServerData data, SourceServerData sourceData)
        {
            var result = false;

            if (data.Type == sourceData.Type)
                result = true;

            if (data.IP != sourceData.IP)
                result = true;

            //other checks/rules to validate incoming data

            return result;
        }

    }

Wait here to think what is wrong with above code…

Any guess…

Ok, lets discuss now, take a look into class ValidateData first 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 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 need 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 to for extensions. In other hand if someone need to modify the behavior to persist data, need to change actual class.

In 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, lets 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, its 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 above snippet! In above we have a ValidateData class, which is only responsible to validate data by certain validations/rules.

With 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 source-code?

You can download complete souce code of examples used in this article from GitHub: Learning Solid.