Solidifying your code using SOLID Programming Principles

Contents

Abstract

The S.O.L.I.D programming principles are not new, unfortunately I wasn’t aware to these principles until I read few articles of Mr. Shivprasad Koirala. In this article we will understand these great programming principles.

Myths related to S.O.L.I.D

I found there are few myths related to SOLID which mess all the things in aspect to Architecture, Design Pattern etc.

There are many kinds of myths, some related to structuring of program and others are related to Design Patterns

Lets discuss few of these.

I know OOPS then why I learn S.O.L.I.D

This is a biggest mistake when someone correlate S.O.L.I.D to Object Oriented Programming. Earlier, I was one of these people who believe in this myth J OOPs is a programming paradigm based on concepts of Object while S.O.L.I.D. are the programing principles which tell us how to write good program.

I Know Design Patterns then why I learn S.O.L.I.D

Again a mistake, design patterns are telling how to design our programs/software but on the other hand S.O.L.I.D are only principles to make our program clean.

SOLID Principles are only applicable to .NET/C# and not for Java

One greater misunderstanding about these Principles a myth, which says these principles are not applicable for Java. Ah! what a myth :). These Principles are not related to any programming language or in other words these principles are not built for any specific programming languages. These principles are just kind of guidelines to make our code/program robust and it does not matter in which language the program has been written.

As an Architect, do I really need to care about S.O.L.I.D?

It is purely un-realistic if someone say that these principles are not for Architects. As an architect - you should think about a robust design, scalability, and components distribution.

While designing any software/application, one should keep in mind all these principles. I agree there would chances for overlapping but to make application design robust one should follow these.

These principles are applicable for all who involved in Technical part of the Software.

  • A developer - needs to write a code in way so, he/she must obey these principles
  • A reviewer - should review code/area in a way so, he/she must obey these principles
  • A Tech Lead - should guide the team in a way so, team must obey these principles

There may be long list depending upon the nature and size of a software/application.

I am working in maintenance project, why do I care about S.O.L.I.D?

Interesting one, it vary project to project how one could implement these principles. But, we can't say that in a maintenance type of project we neglect these principles.

I would take the opportunity to elaborate this with a real scenario example: a long time back, I worked on a maintenance project and the interesting thing is that when I get a part of that team, the project was almost done. I got some assignment and I noticed a much repeated code through-out the application. I have to complete that assignment in 16Hrs (2days) including QA's efforts.

I approached to my Team Manager, unfortunately my manager wasn't convince from me, his words was "we are almost done with this project, I do not care about the SOLID principles, also, we have received a Green flag from our users/clients. If you can't complete this task within time limit then I have to assign this task to someone else, choice is yours :(" .

It wasn't my day that day. Can you imagine, what did I do that time?

Many of us will definitely complete the task within a stipulated time period. We are developers and we don't want to leave tasks.

Of course, I did not leave that task, I completed the same before time period but in my way of style. I wrapped up a new class and added new functionality related to my task, wrote it in a way of S.O.L.I.D. also, attached the similar things with this class so, other area of code could feel S.O.L.I.D. When I sent my changes for review, I sent with these notes:

"I noticed lot of code is repeating itself and some places are yak, need to clean up the things. I implemented my changes by obeying S.O.L.I.D. Principles".
Can you imagine what would be the reply of code review?

Ah! I got a reply from a reviewer, who was much senior to me quoting notes "Gaurav, I am reviewing this project from last 17-months and did not find any discrepancies. However, I noticed few things in your code as mentioned below, request you to make appropriate changes."

I made all the requested changes, which are just cosmetic things not related to S.O.L.I.D or my way of coding and my changes get approved after QA and deployed with release.

Now, think the story about this incident, we got a call for code-review by the client. We have a meeting, I wasn't the part of that meeting only few senior guys of our team, attended.

Suddenly, I got a phone call on y desk from my manager with words "Come at conference hall, client is calling". It was a video conference call, and suddenly my manager introduced me to client.

I was shocked when client asked about my work experience and the approach why should I write the code in this way. Crap man! I was not in my zone that time.

Suddenly client clapped and said 'great, you're done it in a good manner'. ...

My motto to give the above scenario is just, what if I felt that I am a developer and working in a maintenance project also, my project is having final release, why should i bother about all these principle.

As, I said, it depends/vary project-to-project that how to implement these principles, but it is recommendable never forget these principles.

I am a Quality Assurance person and working with Selenium, why do I care about S.O.L.I.D?

Recently, I attended a seminar of an Engineering College (for their Science Fest), there I got asked this shocked question from one of QA developer.

In my view, QA developer should care about SOLID.  As a specific scenario here for Selinium, so, here QA developer has to write all code to automate his/her QA process. In this scenario we should care about SOLID.

I will discuss in detailed later on with more write-up. I have few examples which prove above.

Note: There might be more myths, if anyone in your mind please add here and we will definitely discuss those.

Introduction

Object Oriented Programming (OOPs) provide us the way to write, polish our program in a better shape and way. These are the basic points which guide us how to write a good program.

For great Object Oriented Designs (OOD) we need to think beyond this. S.O.L.I.D principles provide us, a way to write great design, yes there are certain design patterns, which guide us the same but SOLID is much before these patterns.

Lets discuss and understand all using C# code snippets as code examples:

Defining S.O.L.I.D

This 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

Learning Single responsibility principle (SRP)

Name of this principle described itself, it should have single responsibility. Who should have single responsibility? Here, we are studying/learning principles to design best classes/programs/systems.

In reference to this I would say "A class should have single responsibility". Let’s dive into ocean - can we read this like "a class should not design to do multiple activities", so, what kind of activities

Let’s think I have to design a system which provide me employee details, so, it should include activities, general I have CRUD (Create Read Update Delete) operations. Now, as per Single responsibility principle, I have to design a class, which should do any of these operations but not all of these J

I am remembering my old days, when I was learning C++. Generally, I was writing 1000s lines of code in a one program contains many if..else. At that time I was happy to run these programs.

Now, today's I do not like a method which contains more than 4-5 lines, how world changed?

When I was learning this principle, question was in my mind, why class should not responsible for multiple responsibility? I found the answer as:

More, responsibility tied classes towards more changes in future.

Yes, this is true, if I designed a class (I will explain using my old days code in coming learning part of S.O.L.I.D), which is responsible to modify data, retrieve data and then save data. In future if there is

Such kind of business requirements, where our modification or data retrieval logic would be changed then we have to change our classes many time and at many places, this would be encountered more bugs and more code changes.

Now, take a look in the following snippet:

public class DataMigrater

{

public IList<ServerData> GetData(DateTime initialDate, DateTime endDate)

{

//get server data within date range



return new List<ServerData>();

}

public IList<ServerData>ProcessData(IEnumerable<ServerData>rawData)

{

//apply rules



return rawData.ToList();

}


public void Migrate(IEnumerable<ServerData> rawData)

{

//migrate processedData from server to server

}

}


class Program

{

private static readonly DateTime StartDate = new DateTime(2014, 07, 01);

private static readonly DateTime EndDate = DateTime.Now;

static void Main(string[] args)

{

var dataMigrater = new DataMigrater();



//get raw data

var rawData = dataMigrater.GetData(StartDate, EndDate);


//process raw data

var processedData = dataMigrater.ProcessData(rawData);


//finally migrate processed data

dataMigrater.Migrate(processedData);

}

}

In our class DataMigrater is having too many responsibilities. This class:

  • Fetch data
  • Process data
  • then, Migrate data

So, as per SRP our class is doing wrong. Here, we are not following S.O.L.I.D. What should our class do? Let’s start from name of class i.e. DataMigrater, this looks me to class should be responsible only to migrate data. So, class should not be concerned about what and how data is coming for migration.

One more reason, class should be responsible for one thing - let’s think a scenario where method

public void Migrate(IEnumerable<ServerData> rawData){ }

Throws an exception and our data not get migrated. Now, we need to verify all three methods because we are not sure if data is well fetched or well processed. This laid us for many burdens.

Do you want to bear this load? I am sure, no developer want to bear this J

Now, a big question is 'how to do that?' - take a look into following snippet:

public class DataMigrater

{

private readonly IList<ServerData> _data;

private readonly IServerDataRepository _repository;

private readonly ILog _logger = LogManager.GetLogger(typeof(DataMigrater));

public DataMigrater(IList<ServerData> data, IServerDataRepository repository)

{

_data = data;

_repository = repository;

}

public void Migrate()

{

try

{

foreach (var data in _data)

{

var stopWatch = Stopwatch.StartNew();

Migrate(data);

stopWatch.Stop();

_logger.InfoFormat("Total data {0} migrated in {1}", _data.Count, stopWatch.Elapsed);

}

}

catch (Exception ex)

{

_logger.Error(ex);

throw;

}

}

private void Migrate(ServerData data)

{

try

{

//Migrate data from server to server

_logger.InfoFormat("Migrating data with Id:{0}", data.Id);

}

catch (Exception ex)

{

_logger.ErrorFormat("An exception occurred attempting to migrate data with Id:{0}", data.Id);

throw;

}

}

}

Now, our class is having only one method Migrate(). You noticed there are lot of changes have been made in this class, we will discuss all one-by-one in coming articles. As of now let’s concentrate on Single Responsibility Principle.

In above, our class is now only concern to Migrate Server data. This class is not bothered whether supplied data is processed or raw, there are other classes responsible for these things now.

See below:

public class ServerProcessedOrRawDataQuery

{

internal IServerDataRepository Repository { get; set; }

public ServerProcessedOrRawDataQuery(IServerDataRepository repository)

{

Repository = repository;

}

public IQueryable<ServerData> Query(DateTime startDate, DateTime endDate)

{

return new ServerDataQuery(Repository).ProcessedData()

.Where(d => d.InitialDate <= endDate && d.EndDate >= startDate);

}

}

public class ServerDataQuery

{

internal IServerDataRepository Repository { get; set; }

public ServerDataQuery(IServerDataRepository repository)

{

Repository = repository;

}

public IQueryable<ServerData> Query()

{

return Repository.Get().AsQueryable<ServerData>();

}

public IQueryable<ServerData> ProcessedData()

{

return Query().Where(d => d.IsDirty == false);

}

}

Now, our program is changed as:

class Program

{

private static readonly DateTime StartDate = new DateTime(2014, 07, 01);

private static readonly DateTime EndDate = DateTime.Now;

static void Main(string[] args)

{

var repository = new ServerDataRepository(); //we can use any DI to initiate our repositories

var processedData = GetProcessedData(repository);

var dataMigrater = new DataMigrater(processedData,repository);

Console.WriteLine("Data migration started...");

dataMigrater.Migrate();

Console.WriteLine("Data migration completed...");

Console.ReadLine();

}

private static IList<ServerData> GetProcessedData(IServerDataRepository repository)

{

return new ServerProcessedOrRawDataQuery(repository).Query(StartDate, EndDate).ToList();

}

}

Finally, we can define Single responsibility principle as:

"Class should be designed for single responsibility and there should not more than one reasons to make changes in this class. The responsibility of this class should be completely tide/encapsulated by the class."

Learning Open/closed principle (OCP)

When, I read this principle, I thought it looks like that my class should be open or closed, I thought either one. But when I read following definition from wiki:

"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"

I was shocked to think how it is possible to make my class open and closed and not open or close, I thought in other words how can I allow things to modify without doing actual modifications to my object, it was just confusing to me L

I dive into OOPs (Object Oriented Programming) for answer to my question. Let’s think about abstraction; we can create a base class and overridable functions with different behavior. Yes, we can allow changes by keeping objects unchanged.

Let take an example: We have to send an email for different operations body of emails depend upon certain business rules and can contain different or same messages. Now, what we can do here

We can create a class like CreateEmail or whatever you want to name it, with one method BuildMessage. So, this class is only and only responsible to build email messages as per different logics as this method is overridable I can define it functionality per my choice.

Isn't it look like very interesting and easy J

In continues with our above code of 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;

}

}

Can you think, what is wrong with above code?

Let’s 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, let’s try to do something by creating an interface:

public interface IDataValidator

{

bool Validate(ServerData data, SourceServerData sourceData);

}

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;

}

}

Above code snippet is self-explanatory, where we defined IDataValidator which is having a method Validate.

Method name describes itself, its a part of DataValidator so, it should validate data so, it named as ValidateJ

Now, redesign our class ValidateData:

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 yourselfJ

Learning Liskov substitution principle (LSP)

Here is definition from wiki:

"if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substituted for objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)"

I understood above definition like this: parent should easily replace the child object.

To understand it bit more, let’s look into example of EmailNotifications(considered in OCP, above), now let’s say we need to also send this email for print what we can do?

We can create a new class let’s call it NotificationsForPrint or whatever name you would like J it inherits our class EmailNotifications. Now, out both classes base and child class having at least one similar methods.

Can we use our child class to substitute our base class, no, in this situation never? So, we need to use inheritance, define two separate interfaces one is building message and another one is sending message and let’s decide in implementation where for what we need to build and send messages.

Take a look into following snippet:

public class DataBase

{

public virtual bool IsValid(ServerData data, SourceServerData sourceData)

{

return new Validator(new List<IValidator>()).Validate(data, sourceData);

}

public virtual void Save()

{

//logic to save data

}

}

public class ProdDB : DataBase

{

public override bool IsValid(ServerData data, SourceServerData sourceData)

{

return base.IsValid(data, sourceData);

}

public override void Save()

{

//logic to save data

base.Save();

}

}

public class QADB : DataBase

{

public override bool IsValid(ServerData data, SourceServerData sourceData)

{

return base.IsValid(data, sourceData);

}

public override void Save()

{

//logic to save data

base.Save();

}

}

public class LocalDB : DataBase

{

public override bool IsValid(ServerData data, SourceServerData sourceData)

{

return base.IsValid(data, sourceData);

}

public override void Save()

{

throw new Exception("Local Data should not be saved!");

}

}

Recall, inheritance and you can visualize that DataBase is a parent class of ProdDB, QADB and LocalDB.

Lets think polymorphism for a while and we can write as:

DataBase pDataBase = new ProdDB();

DataBase qDataBase = new QADB();

DataBase lDataBase = new LocalDB();

//also can create a list of DataBase type

var dataBases = new List<DataBase> {new ProdDB(), new QADB(), new LocalDB()};

Isn't it easy to save my object using this?

var dataBases = new List<DataBase> {new ProdDB(), new QADB(), new LocalDB()};

foreach (var dataBase in dataBases)

{

if (dataBase.IsValid(data, sourceData))

dataBase.Save();

}

Wait, wait...

What’s wrong in above, NOTHING?

Yes, you are absolutely correct there is nothing wrong with above code, the only thing is, its execution. When above code execute, it will also invoke save method of LocalDB object. In this case we received an exception as our LocalDB object is not supposed to save data.

A big question is "why this happened?"

In simple words LocalDB is actually not an entity of DataBase or we can say DataBase is not an actual parent of LocalDB.

Another question in mind "Why LocalDB is not an entity of DataBase, when it inherits DataBase".

Hold on, go back to LocalDB class and check this is not meant to implement Save() method, here this makes LocalDB as a separate entity.

In simple words, LISCOV says parent should easily replace its child.

How to implement LISCOV principle?

We know LocalDB is not supposed to save data but others are. Let’s consider following snippet:

public interface IRule

{

bool IsValid(ServerData data, SourceServerData sourceData);

}

public interface IRepository

{

void Save();

}

Now, we have two interfaces, with their own methods. IRule: to validate data and IRepository: to save/persist data.

Let’s make changes to our LocalDB class, as:

public class LocalDB : IRule

{

public bool IsValid(ServerData data, SourceServerData sourceData)

{

return new Validator(new List<IValidator>()).Validate(data, sourceData);

}

}

Why we implement IRule?

For LocalDB, we only want to check whether data is valid or not. We do not want to persist data in any scenario.

Now, we can't write this:

DataBase lDataBase = new LocalDB();

Our DataBase class, should be like this:

public class DataBase : IRule, IRepository

{

public virtual bool IsValid(ServerData data, SourceServerData sourceData)

{

return new Validator(new List<IValidator>()).Validate(data, sourceData);

}

public virtual void Save()

{

//logic to save data

}

}

Other classes will remain unchanged.

Our execution code goes as:

public void Execute(ServerData data, SourceServerData sourceData)

{

var dataBases = new List<IRepository> { new ProdDB(), new QADB() };

foreach (var dataBase in dataBases.Where(dataBase => ((IRule)dataBase).IsValid(data, sourceData)))

{

dataBase.Save();

}

}

Now, our code is looks too easier to handleJ

Learning Interface segregation principle (ISP)

Here is a definition from wiki:

"No clients should be forced to implement methods which it does not use and the contract should be broken into small and more specific interfaces."

I took this correct as: "as a client why should I implement 9-methods of interface when I need only 3-methods", isn't it make developers life easy J

This also similar to High Cohesion Principle of GRASP.

Think, can we consider our EmailNotification example (for both scenarios print and send via smtp server)?

Let’s explore this with an example:

First of all go back and take a look into code example discussed in LSP, there some of our databases are getting saved after validation. Now, think a scenario there are more databases and for these additional databases we require a report in other words new databases needs to be read and saved.

In very first instance, I can think to add a new method to interface IRepository (which can read data or generate report).

public interface IRepository

{

void Save();

void Generate();

}

Do you think, above approach is good?

Think, think and again think.... 🙂

By adding new method to an existing interface, we are forcing to use new method to all those classes, who are implementing this interface. But those classes are not supposed to use newly added method. So, my ProdDB class look like:

public class ProdDB : DataBase

{

public override bool IsValid(ServerData data, SourceServerData sourceData)

{

return base.IsValid(data, sourceData);

}

public override void Save()

{

//logic to save data

base.Save();

}

public override void Generate()

{

//Report generation logic

}

}

But actually, ProdDB class does not require to Generate report, but with above implementation this class have to implement new Generate() method.

Here, we are forcing our class to implement that method, which this class does not want.

So, we are not following Interface Segregation Principle in our above code [go top and read ISP definition :)].

What is the solution for this problem?

First, try to segregate our IRepository interface.

public interface IReport:IRepository

{

void Generate();

}

To segregate, I created another IReport interface with new method Generate(). Now, we have two separate interfaces IRepository and IReport.

Let’s create a new class, which is meant for those clients, who wants to generate report:

public class DataBaseReport : IReport

{

public void Save()

{

var dataBase = new DataBase();

dataBase.Save(); // we want to save data

}

public void Generate()

{

//implement report generation logic here

}

}

At this point, we have two different classes DataBase and DataBaseReport, one is for those clients who don’t want to generate report and another is who wants to generate report 🙂

So, our execute method would look like:

public void Execute()

{

//Old client implementation

IRepository repository = new DataBase();

repository.Save();

//implementation for new clients who want to generate report

IReport report = new DataBaseReport();

report.Generate();

}

You can see, how we resolved the problem. This solution will very useful when millions of clients need different things while our existing clients don't J

Learning Dependency inversion principle (DIP)

This principle remembers us Decoupling:

"High level modules should not depends upon low-level modules both should be tide using abstractions"

What does this mean?  Let’s take a look into our EmailNotification example once again and think why should our code decide where to send my email (smtp server or printer) at very beginning.

Why not it should automatically perform preferable action [we will take a look in details - in coming parts]? Till then take a look into this pattern on wiki.

Let’s explore this with an example:

Rewind code example we discussed Single Responsible Principle, there is a property Type, which tells data type, now take a look into following snippet

public class DataBase : IRule, IRepository

{

private ISession session;

public virtual bool IsValid(ServerData data, SourceServerData sourceData)

{

//implementation goes here

}

public virtual void Save(ServerData data)

{

if(data.Type == 1)

{

session = new ServerDataSession();

}

else

{

session = new SourceServerDataSession();

}

session.Save(data);

}

}

There is violation of SRP in above, to solve this lets create a separate interface which holds, save method:

interface ISession

{

void Save<T>(T data);

}

and lets create different saver as per below snippet:

class ServerDataSession:ISession

{

public void Save<T>(T data)

{

//save logic goes here

}

}

class SourceServerDataSession:ISession

{

public void Save<T>(T data)

{

//save logic goes here

}

}

Now, what?

We need to supply Session as per Data type, here we are delegating responsibility to someone else. In other words, for above snippet our DataBase class is delegating its responsibility to others (ServerDataSession,SourceServerDataSession). Need to modify our DataBase class as:

public class DataBase : IRule, IRepository

{

private ISession _session;

public DataBase(ISession session)

{

_session = session;

}

public virtual bool IsValid(ServerData data, SourceServerData sourceData)

{

//implementation goes here

}

public virtual void Save(ServerData data)

{

_session.Save<ServerData>(data);

}

}

At this point, our client is free to inject what he/she wants to consume:

IRepository dataBase = new Database(new ServerDataSession());

Conclusion

We learned S.O.L.I.D. programing principles to make our applications/software design better. This is just a start, as soon as you go through the other Design Patter you will grab more power to make your software more beautify. Complete source code is also available at: Learning SOLID

Leave A Reply