DEV Community

arpitmandliya
arpitmandliya

Posted on • Edited on • Originally published at java2blog.com

A guide to Solid principles in java

In this post, we will see 5 SOLID Principles in Java.

Robert C. Martin gave five objected oriented design principles, and the acronym "S.O.L.I.D" is used for it. Each letter of this acronym talks about principles in Java. When you use all the principles of S.O.L.I.D in a combined manner, it becomes easier for you to develop software that can be managed easily. The other features of using S.O.L.I.D are:

  • It avoids code smells
  • Quickly refractor code
  • Can do adaptive or agile software development

When you use the principle of S.O.L.I.D in your coding, you start writing the code that is both efficient and effective.

What is the meaning of S.O.L.I.D?

As stated above, S.O.L.I.D represents five principles of Java which are:

  1. S: Single responsibility principle
  2. O: Open-closed principle
  3. L: Liskov substitution principle
  4. I: Interface segregation principle
  5. D: Dependency inversion principle

The article discusses each of this principle in-depth. We will start by considering the very first principle, which is single responsibility principle.

Single Responsibility Principle (SRP)

According to the single responsibility principle, there should be only one reason due to which a class has to be changed. It means that a class should have one task to do. This principle is often termed as subjective.

The principle can be well understood with an example. Imagine there is a class which performs following operations.

  • connected to a database
  • read some data from database tables
  • finally, write it to a file.

Have you imagined the scenario? Here the class has multiple reasons to change, and few of them are the modification of file output, new data base adoption. When we are talking about single principle responsibility, we would say, there are too many reasons for the class to change; hence, it doesn't fit properly in the single responsibility principle.

Open Closed Principle

According to open closed principle, entities or objects should remain open for extension, but they should stay closed for modification. To be precise, according to this principle, a class should be written in such a manner that it performs its job flawlessly without the assumption that people in the future will simply come and change it. Hence, the class should remain closed for modification, but it should have the option to get extended. Ways of extending the class include:

  • Inheriting from the class
  • Overwriting the required behaviors from the class
  • Extending certain behaviors of the class

An excellent example of open closed principle can be understood with the help of browsers. Do you remember installing extensions in your chrome browser?

Basic function of chrome browser is to surf different sites. Do you want to check grammar when you are writing an email using chrome browser?If yes, you can simply use Grammarly extension, it provides you grammar check on the content.

This mechanism where you are adding things for increasing the functionality of the browser is an extension.  Hence, the browser is a perfect example of functionality that is open for extension but is closed for modification. In simple words, you can enhance the functionality by adding/installing plugins on your browser, but cannot build anything new.

Let's take another example.
You are using any Spring function functionality. You can obviously can not change core logic of it but you can extend Spring framework classes and create your own one.

Liskov Substitution Principle

Liskov substitution principle assumes q(x) to be a property, provable about entities of x which belongs to type T. Now, according to this principle, the q (y) should be now provable for objects y that belongs to type S, and the S is actually a subtype of T. Are you now confused and don't know what Liskov substitution principle actually mean? The definition of it might be a bit complex, but in fact, it is quite easy. The only thing is that every subclass or derived class should be substitutable for their parent or base class.

You can say that it is a unique object-oriented principle. The principle can further be simplified by understanding this principle; a child type of a particular parent type without making any complication or blowing things up should have the ability to stand in for that parent.This principle is closely related to Liskov Substitution principle.

Interface Segregation Principle

According to interface segregation principle, a client, no matter what should never be forced to implement an interface that it does not use or the client should never be obliged to depend on any method, which is not used by them.

So basically, the interface segregation principles as you prefer the interfaces, which are small but client specific instead of monolithic and bigger interface.

In short, it would be bad for you to force the client to depend on a certain thing, which they don't need.

Let us now again take an example to understand this.

Let's take a simple example. You are implementing your own ArrayList and LinkedList in java. You create an interface called List which both classes will implement.

package org.arpit.java2blog;

public interface List<T> {
    public T get();
    public void add(T t);
    public T poll();
    public T peek();    
}
Let's create LinkedList class now.
package org.arpit.java2blog;

public class LinkedList implements List{

    @Override
    public Integer get() {
        // Implement this method
        return null;
    }

    @Override
    public void add(Integer t) {
        // Implement this method
    }

    @Override
    public Integer poll() {
        // Implement this method
        return null;
    }

    @Override
    public Integer peek() {
        // Implement this method
        return null;
    }
}

Let's create ArrayList class now.

package org.arpit.java2blog;

public class ArrayList implements List{

    @Override
    public Integer get() {
        // Implement this method
        return null;
    }

    @Override
    public void add(Integer t) {
        // Implement this method
    }

    @Override
    public Integer poll() {
        // ArrayList does not require this method
        return null;
    }

    @Override
    public Integer peek() {
        // ArrayList does not require this method
        return null;
    }
}

Do you see the problem, even though you do not require poll and peek method in ArrayList, we have implemented them.
The correct solution for above problem will be:
Create another interface called Deque which will have peek and poll method.

package org.arpit.java2blog;

public interface Deque<T> {
    public T poll();
    public T peek();    
}

And remove peek and poll from list interface.

package org.arpit.java2blog;

public interface List<T> {
    public T get();
    public void add(T t);   
}

Let's change LinkedList class now.

package org.arpit.java2blog;

public class LinkedList implements List,Deque{

    @Override
    public Integer get() {
        // Implement this method
        return null;
    }

    @Override
    public void add(Integer t) {
        // Implement this method
    }

    @Override
    public Integer poll() {
        // Implement this method
        return null;
    }

    @Override
    public Integer peek() {
        // Implement this method
        return null;
    }
}

Let's change ArrayList class now.

package org.arpit.java2blog;

public class ArrayList implements List{

    @Override
    public Integer get() {
        // Implement this method
        return null;
    }

    @Override
    public void add(Integer t) {
        // Implement this method
    }
}

As you can see, we have seggregated two interface to achieve required functionality.

Dependency Inversion Principle

According to this, dependency inversion principle, entities should depend only on abstractions but not on concretions. According to it, the high-level module must never rely on any low-level module but should depend on abstractions. Let us again understand it through another practical example.

You go to a local store to buy something, and you decide to pay for it by using your debit card. So, when you give your card to the clerk for making the payment, the clerk doesn't bother to check what kind of card you have given. Even if you have given a Visa card, he will not put out a Visa machine for swiping your card. The type of credit card or debit card that you have for paying does not even matter; they will simply swipe it. So, in this example, you can see that both you and the clerk are dependent on the credit card abstraction and you are not worried about the specifics of the card. This is what a dependency inversion principle is.

Wrap Up

I hope that now you know the basic definition of all the five components of S.O.L.I.D, which are single responsibility principle, open, closed principle, Liskov substitution principle, interface segregation principle, and dependency inversion. So, basically, whenever you write code, you have to keep these core principle in your mind and actually, to be honest, you have to take these principles as your base map while writing those codes to make it efficient and effective.

The management of the code written by using these principles is also an easy task to do. Using this principle initially in your coding might look odd but you need to practice writing your codes using these principles, and gradually it will become a part of you. And then, the code that you have written can be easily modified, extended, refactored, or tested without facing any problem according to the requirement that you meet. So practice this principle whenever possible to make your coding better.
Source: Solid principles in java

You may also like:
xms and xmx parameter in java
Java reached end of file while parsing
NumberFormatException in java
FileNotFoundException in java
Check if number is numeric in java

Top comments (7)

Collapse
 
riccardo_cardin profile image
Riccardo Cardin

I think that you should treat each principle more in detail, and isolated. The SRP is one of the more misunderstood principles in the history of programming. The simple word "reason to change" is not self-explanatory, and it requests to be contextualised. When you speak about the SRP, you must always refer to cohesion.

Please, try to have a look at this post of my blog, and let me know what do you think: Single-Responsibility Principle done right.

Collapse
 
arpitmandliya profile image
arpitmandliya

Awesome Explanation! Thank you Riccardo.
Yes, I do agree each principle deserves separate post.

Collapse
 
daanwilmer profile image
Daan Wilmer

To me, the Liskov Substitution Principle (LSP) was best explained as "require no more, ensure no less."

Require no more: any valid input for the superclass must be valid input for the subclass. For example: suppose you have a class MyClass with a method totalLength(List<String> strings). If you have subclass MySubClass extends MyClass, then having the method totalLength only accept instances of ArrayList (and fail when using a LinkedList) would violate this principle. If the subclass would allow more than just lists, and accept any Collection<String>, then this would be perfectly fine according to the LSP. The compiler might complain (although it's been a while since I wrote Java so I don't know for sure), but it perfectly fits within the LSP.

Ensure no less: for the return value, it should be the other way round. If the superclass has a method List<String> getNames(), then a valid substitution in the subclass might be ArrayList<String> getNames(). Again, not sure if Java allows it, but as long as the return value is an instance of the return type defined in the superclass, the LSP would allow any promise. You're ensuring that it returns a List<String>, and it even is an ArrayList<String>!
If, on the other hand, your subclass would want to return a Collection<String>, this is not allowed under LSP. You could be returning a List<String>, but it could also be a HashSet<String>, which does not implement the List<String> interface. You would no longer ensure that it is a List.

I think Java prevents (some form of) LSP violations by requiring the signatures to be exactly the same, but there might be ways to try and circumvent them. Please don't :)

Collapse
 
arpitmandliya profile image
arpitmandliya

Thank you for the good explaination.
Yes, Java prevents LSP violations while overriding the method. It does not allow you to change return type, however you can return instance of subtype also known as Covariant return type.

Collapse
 
dbanty profile image
Dylan Anthony

Great explanation! I always thought that “open, closed principle” was impossible I Python because everything can be modified. But your explanation of the intent of writing the code makes a lot of sense.

Collapse
 
arpitmandliya profile image
arpitmandliya

Thanks Dylan :-)

Collapse
 
bennykelly profile image
bennykelly • Edited

Great post, thanks for sharing. Does anyone know of any interactive sites/apps to practice SOLID principles? It's something I want to focus on in the coming months, and I find those browser based IDE tools hugely beneficial for picking up new practices. Codebashing for security is the perfect example, just looking for something similar around SOLID principles