Learn SOLID Principles in 10 Minutes: Real-World Code Snippets
For Developers

February 15, 2024

Learn SOLID Principles in 10 Minutes: Real-World Code Snippets

SOLID stands for 5 basic principles that can be applied in object-oriented programming and design to make your code maintainable, flexible, testable, understandable, and more robust.

These principles were introduced by Robert C. Martin, an American software engineer, author of the agile manifesto, software design guru, and best-selling author.

Back when I was a student and started with programming I heard the term “SOLID principles” a lot.

For a long time, I was running away from SOLID principles as they were really confusing and kinda scary for me.

After I spent a lot of time refactoring my code multiple times and being frustrated by that knowing that I am doing something wrong, I decided to face the reality and learn them.

SOLID principles are not something you can just easily read one time and consider that you know them.

You need to apply them with every JIRA ticket that you complete in your day-to-day job. You need to keep them in mind all the time, and it takes time and practice.

SOLID principles are:

  • The Single Responsibility Principle
  • The Open-Closed Principle
  • The Liskov Substitution Principle
  • The Interface Segregation Principle
  • The Dependency Inversion Principle
Calling all software engineers seeking to work remotely! Join Index.dev remote-work platform and secure long-term full-time remote jobs with leading tech companies →

1) Single Responsibility Principle (SRP)

This principle states “a class should do one thing and therefore it should have only a single reason to change.”

So from real-world examples, we can take a spoon and fork as an example. Each of these eating tools is doing only one thing.

Now imagine that you have a spoon and fork merged in one tool and you get spoon-fork or maybe a better name is spoork. It would be awkward and probably hard to use.

I did some research and if you like to violate the single responsibility principle in real life, you can order this monstrosity for circa 4 bucks at AliExpress here.

Now let’s go to the code example. First I will show a bad example, then explain why it’s bad, and then show a good one. All examples are in TypeScript.

We can see in the example above that this class is doing 2 things:

  • it is used as a model for patient data
  • it can generate PDF reports for patient data

To fix the single responsibility principle, code should be split into 2 classes:

Patient and ReportGenerator are now 2 different classes.

The patient is used as a model for patient data, while ReportGenerator can generate a PDF report for patient data.

Tomorrow, if you need to implement an Excel spreadsheet report from patient data, things will be much easier with this modification.

2) Open-Close Principle (OCP)

This principle says that classes should be open for extension and closed to modification.

So what this principle means is that we should be able to add new features to the class without affecting the old code.

This is because whenever we edit old code, we run the risk of introducing potential problems. So, if at all possible, we should avoid touching the proven and trustworthy (mainly) production code.

A good example of this is a mixer and its extensions. You can attach an extension to the mixer and use it without digging inside the mixer’s encapsulated body.

Now let’s see the code example. First, a bad example.

Above we can see the code which calculates the game rating points for 2 types of football players, Midfielder and Goalkeeper.

The problem here is that if we add a new type of player, for example, a defender, we will need to add a new method in GameRatingCalculator class.

So it’s better to add a new interface respecting the open-close principle.

Here is the improved version of the code:

With the introduction of the new player type GameRatingCalculator class now doesn’t have to be changed.

This is achieved with an interface that consists of common things across the player types.

3) Liskov Substitution Principle (LSP)

According to this principle, a program’s objects should be able to be replaced with instances of their subtypes without affecting the program’s correctness.

Good examples of the Liskov substitution principle are animals. For example, a duck is a bird that can fly, swim and walk.

On the other hand, the dog is also an animal, but it can’t fly, it can only walk and swim.

So for both dogs and ducks, we can say that they are animals, keeping in mind their natural behavior correctness.

Here is the code example:

We can see in the above example that the Liskov substitution principle is violated since both Goalkeeper and Defender extends Player class which implies that both types of players can kick the ball with their feet and hold the ball with their hands.

This is correct for the Goalkeeper, which can use both feet and hands in the game within his goalkeeper zone.

But for Defender, he can only use his feet in the game as playing with hands for all the players except the goalkeeper is forbidden in football (soccer).

Let’s correct this code:

Now every type of player can only perform allowed in-game actions.

Calling all software engineers seeking to work remotely! Join Index.dev remote-work platform and secure long-term full-time remote jobs with leading tech companies →

4) Interface Segregation Principle (ISP)

The interface segregation principle states that no code should be forced to depend on methods it does not use. No client should be forced to rely on methods that they do not employ.

A good real-life example of a company that respects this principle is Apple with its dongles. Just look at these things:

So for each type of cable, you have a specific dongle.

I am not so good at explaining why they introduced this change, but the legendary “El Risitas” explained it here perfectly.

Now let’s jump to the code example:

The problem here is that we violate the interface segregation principle by using all methods in one interface instead of multiple interfaces.

Let’s correct that:

With the separation of interfaces, we can specify which actions a specific entity can perform.

5) Dependency Inversion Principle (DIP)

This one is a bit confusing.

It says depend upon abstractions, not concretions. It states that our classes should depend upon interfaces or abstract classes instead of concrete classes and functions.

One good real-world example is a plug and power socket.

It doesn’t care if the wires behind the power socket are from aluminum or copper to do the job.

Why?

Because it doesn’t depend upon concretions (type of wires behind power socket) but upon abstraction (type of power socket, it can be EU/US).

Now, let’s look at our code example:

As you can see in the above example, we are really on exact implementations of core classes and their functions.

However, given how similar these classes are and how they will act similarly, this approach is incorrect.

Therefore, there is a better way to meet the conditions necessary to realize the purpose of the Dependency Inversion Principle.

Since we already know all the other principles, it’s even simpler than I originally stated.

We first design an interface named Player. We rely on Goalkeeper, Defender, Midfielder, and Attacker on the Player interface because they are comparable classes.

Instead of initializing Goalkeeper, Defender, Midfielder, and other classes in the FootballMatch class one by one, we take a list of them and call each play() method iteratively.

Conclusion

I hope I could help you brush up on your knowledge or pick up some new information.

My advice is to remember these guidelines as you design, write, and refactor your code to make it much cleaner, more extensible, and testable.

To all the senior developers yearning for remote software jobs with esteemed US and UK companies, your quest ends here. Index.dev opens doors to opportunities that resonate with your expertise. Index.dev engineers relish competitive salaries, exceeding market averages across countries. 

Register now and let the remote revolution redefine your career →