S.O.L.I.D Principles for Efficient Programming

What are the S.O.L.I.D principles

Kavinduchamith
6 min readFeb 24, 2022
solid priciples with examples
S.O.L.I.D Principles(www.kamprasad.com)

Why do we need S.O.L.I.D?

All software engineers and developers scattered over the world are surely able to develop software. But among all of them, only a few people know how to develop software according to object-oriented principles that are defined by the software engineering community.

We all are familiar with the basic Object-oriented principles such as,

• Abstraction

• Inheritance

• Encapsulation

  • Polymorphism

But today I am going to discuss the object-oriented principles that are at a higher level than the basic OOP concepts. Those are Known as S.O.L.I.D principles.

What is S.O.L.I.D?

S.O.L.I.D Principles(blog.aboelkassem.me)

S.O.L.I.D are five object-oriented principles that should follow when designing software. Without these principles, we can also develop software, but with these principles, we can develop software with high usability and high maintainability.

So let’s see what are the five principles that indicate from the S.O.L.I.D term.

S — Single Responsibility principle

O — Open and close principle

L — Liskov substitution principle

I — Interface segregation principle

D — Dependency inversion principle

Now let’s discuss these principles one by one with examples.

Single Responsibility principle

Single Responsibility principle
Single Responsibility principle(uploads.toptal.io)

Definition for this principle is,

“A class (function/module/API) should have one and only one reason to change, that means a class should have only one job”

By looking at the definition it is very hard to understand the concept behind this principle. so let's discuss this principle with simple terms with examples. Let’s look at the Employee class that is available in this code segment.

public class Employee {
public void printDetails() {}
public double calculateSalary () {}
public void addEmployee () {}
}

Employee class is responsible for printing details, calculating salary, and adding a new employee to the system. so that means this class has multiple responsibilities and there have three reasons to change the class.

So, this class does not follow the single responsibility principle. To apply the principle, we need to divide this class into three classes each with separate responsibilities.

Open and close principle

Open and close principle
Open and close principle(usercontent.one)

Definition for this principle is,

“Software entities should be open for extension, but closed for modification”

Now let’s look at what is the concept behind this principle. As an example, let’s look at the calculateArea function given below.

public class Shape {
public double calculateArea(String shape) {
double area = 0;
if (shape.equals(“square”)) {
area = 100 * 100;
}
else if (shape.equals(“rectangle”)) {
area = 100 * 50;
}
else if (shape.equals(“circle”)) {
area = 50 * 50 * Math.PI;
}
return area;
}
}

When we need to add a new shape to the system, we need to modify the calculateArea function with the necessary changes.

That is not a good coding practice, and it is not following the open and close principle. So instead of that, we can change this code segment like this,

public abstract class Shape {
public abstract double calculateArea();
}
public class Rectangle extends Shape {
public double calculateValue() {
double area = 100 * 50;
return area;
}
}
public class Square extends Shape {
public double calculateValue() {
double area = 100 * 100;
return area;
}
}

Here we changed the code segment by making the calculateArea method an abstract method and because of that the Shape class also becomes an abstract class.

From this approach, we can easily add (extend)new shapes to the system without changing the code. So that means now this code segment follows the open and close principle.

Liskov substitution principle

Liskov substitution principle
Liskov substitution principle(stackify.com)

Definition for this principle is,

“Subtypes must be substitutable for their base types”

The concept behind this principle is to use inheritance only when the superclass is replaceable by the subclass in all instances. Do not use inheritance for just saving a few lines of code.

As an example, let’s consider the classes given below,

public class Rectangle {
public double Width;
public double Height;
public void setWidth(double width){}
public void setHeight(double Height){}
public double getWidth(){}
public double getHeight(){}
}
public class Square extends Rectangle{
public void setWidth(double width)//set same value for width,height
public void setHeight(double Height)//same value for width, height
}

In the above example, the Rectangle class cannot be replaceable by the Square class. But in order to reuse the methods and properties of the Rectangle class, we use inheritance in the Square class.

According to the Liskov substitution principle, this is not a good coding practice, and this might lead to misbehaviors in the system.

Interface segregation principle

Interface segregation principle
Interface segregation principle(jfiaffe.files.wordpress.com)

Definition for this principle is,

“The Dependency of one class to another one should depend on the smallest possible interface”

Think of the sicario that we create an interface, and we implement that interface in some classes. In that case interface act as a server and the classes that implement the interface become clients.

In interface segregation principle, it defines those clients should not force to implement interfaces they don’t use. Let’s look at the below example.

public interface Animal {
public void Eat();
public void fly();
}
public class Parrot implements Animal {
public void Eat() { }
public void fly() { }
}
public class Dog implements Animal {
public void Eat() { }
public void fly() { //does not have anything to implement}
}

In this example Dog class does not have any meaningful implementation for the fly method. But to keep the compiler happy we implement the fly method with some dummy implementation.

According to interface segregation, this is not a good coding practice.

The ideal answer for this is instead of using fat interfaces use many small interfaces .like the answer given below.

public interface Animal {
public void Eat();
}
public interface Bird {
public void fly();
}
public class Parrot implements Animal, Bird {
public void Eat() { }
public void fly() { }
}
public class Dog implements Animal {
public void Eat() { }
}

Dependency inversion principle

Dependency inversion principle
Dependency inversion principle(stackify.com)

Definition for this principle is,

“Depend upon abstractions(interfaces) not upon concreate classes”

The concept behind this principle is Higher-level modules(classes) should not depend on lower-level modules (classes). But they should depend on abstractions. Let’s look at the example given below.

public class DBoperations {
public Mysql DBob;
public DBoperations(Mysql db) {
DBob = db
}
public void create() {
DBob.create()
}
public void save() {
DBob.save()
}
public void update() {
DBob.update()
}
public void delete() {
DBob.delete()
}
}
class Mysql {
create(){}
save(){}
update(){}
delete(){}
}

In this example, DBoperations class is the Higher-level class and Mysql class is the lower-level class. These two classes are tightly coupled.

That means DBoperations class is dependent on Mysql class. If we change the Mysql class, it will affect the DBoperations class.

According to the dependency inversion principle, this is not a good coding practice. To solve this problem, the ideal answer is given below.

public class DBoperations {
public DBInterface DBob;
public DBoperations(DBInterface db) {
DBob = db
}
public void create() {
DBob.create()
}
public void save() {
DBob.save()
}
public void update() {
DBob.update()
}
public void delete() {
DBob.delete()
}
}
public interface DBInterface {
create();
save();
update();
delete();
}
public class Mysql implements DBInterface {
//implements the methods
}
public class MongoDB implements DBInterface {
//implements the methods
}

So, applying this solution, the Higher level class does not dependent on the lower-level classes anymore. Instead of it is dependent on abstractions(Interface).

I hope you found this useful. I would like to hear from you so feel free to drop a comment or connect with me via LinkedIn.

--

--