SOLID Principles

SOLID Principles was promoted by Robert C. Martin as a “Coding Standard” for all developers. The main idea is when you applied properly, your code will be easier to read and extendable. You can remember SOLID as a collection of best-practice, object-oriented design principles.

How can you identify if you are coding with a bad design? It is very simple, small changes can result in bugs, complex maintainability, etc.

It is time to review SOLID:

S – Single Responsibility Principle (SRP): There should never be more than one reason for a class to change. O – Open Closed Principle (OCP): Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. L – Liskov Substitution Principle (LSP): Functions that use … references to base classes must be able to use objects of derived classes without knowing it. I – Interface Segregation Principle (ISP): Clients should not be forced to depend upon interfaces that they do not use. D – Dependency Inversion Principle (DIP):

  • High level modules should not depend upon low level modules. Both should depend upon abstractions.
  • Abstractions should not depend upon details. Details should depend upon abstractions.

:: Single Responsability Principle (SRP) :: Each class in your code just need to have one responsibility, just one reason to change. If object has more than one reason to change, well, that is a violation to SRP.

Let’s think in a class you use to connect to databases. You have different functions and implementations in order to get the connection in many engines.

public class Database{
     public Connection getPostgresConnection();
     public Connection getMysqlConnection();
     public Connection getMongoConnection();
     public Connection getSqlServerConnection();
 }

This example is very drastic but we can understand the problem because we have a lot of reasons to change the class. We should not have different implementations to connect to databases.

One possible solution is use an interface and declare a generic method. Example:

public interface class Database{
     public Connection getConnection();
 }

Now, we can create many classes we need for every databases engines.

public class PostgresDatabase implements Database{
     public Connection getConnection(){
         Connection conn…
         return conn;
     }
 }
public class MysqlDatabase implements Database{
     public Connection getConnection(){
         Connection conn…
         return conn;
     }
 }
public class MongoDatabase implements Database{
     public Connection getConnection(){
         Connection conn…
         return conn;
     }
 }
public class SqlServerDatabase implements Database{
     public Connection getConnection(){
         Connection conn…
         return conn;
     }
 }

With this change, we have just one class by database engine in order to get its connection. We have only one reason to change each class. Remember, each class shoud have a single responsibility.

When you apply this principle, your code will be:

  • Easer to understand.
  • Better testeable.
  • Less error prone.

This principle apply at different levels: functions, modules, objects.

:: Open Closed Principle (OCP) ::

This principle is one of the oldest of Object Oriented Design (OOD) and describe that the software entities should be open for extension, but closed for modification. This is, every enttity can allow modifications in its behavior without altering its source code.

It is possible if we extend/inhertance from the class. Software entities should be extendable and add new functionality without change the content, without touch the original code, yes that is allow changes adding new classes.

This principle is about the changes.

:: Liskov Substitution Principle (LSP) ::

The official definition is:

If S is a sub-type of T, then objects of type T may be replaced with objects of type S without, breaking the program. – Barbara Liskov

It is very simple. Imagine we have an interface X and we have two implemented classes, class Y and class Z. This principle means we can use class Y or Z and the client does not observe the difference.

Now, imagine we have the class S inheriting from class H, so we can use S as H but it is not possible use H as S.

:: Interface Segregation Principle (ISP) ::

First, let’s define “Interface” like: – Contract – What clients can see and use

This principle means you should not force to the clients to implement interfaces they do not use. The contract (interface) defines some methods but (as a client), it is not necessary implement them.

If you do not apply this principle correctly, you can have classes with dependencies on others classes they do not need. Imagine a class with a lot of methods with a nice return message: “The method or operation is not implemented” because the interfaces force your class to implement them. Think in create small definitions (interfaces) and you can identify violations to this principle when you find in your code throw NotImplementedException.

Create a new interface with specific methods, separate your interface from fat interface and surely you are going to implement all methods.

:: Dependency Inversion Principle (DIP) ::

In this principle, it’s all about decoupling.

This principle is very famous because frameworks like Spring is developed based on this.

The principle states:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces).
  • Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

To understand these states, replace “high-level modules” with important classes, it is easier than use the original word. So, the important classes should not depend on less important classes.

The second state refers to define the interfaces correctly and from that create the classes that implement the interfaces and not vice versa.