SOLID Principles in Java: A Complete Guide from Theory to Code

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

SOLID Principles in Java: From Theory to Practice

SOLID is an acronym for five fundamental principles of object-oriented programming formulated by Robert Martin (Uncle Bob). These principles help developers create flexible, maintainable, and easily extensible code. In this article, we will break down each SOLID principle with Java examples, from simple theory to real-world practice.



1. Single Responsibility Principle (SRP)

Theory: A class should have only one reason to change. In other words, each class should be responsible for exactly one functionality.

SRP Violation:

public class Employee {    public void calculatePay() { /* calculate salary */ }    public void saveToDatabase() { /* save to DB */ }    public void generateReport() { /* generate report */ }}

Fix:

public class Employee { /* data only */ }

public class PayCalculator { public void calculatePay(Employee emp) { /* ... */ }}

public class EmployeeRepository { public void save(Employee emp) { /* ... */ }}

public class ReportGenerator { public void generate(Employee emp) { /* ... */ }}

Now each class has one clear responsibility, making testing and maintenance easier.



2. Open-Closed Principle (OCP)

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

OCP Violation:

public class ShapeDrawer {    public void draw(String shapeType) {        if ("circle".equals(shapeType)) {            // draw circle        } else if ("rectangle".equals(shapeType)) {            // draw rectangle        }    }}

Fix using polymorphism:

public interface Shape {    void draw();}

public class Circle implements Shape { @Override public void draw() { /* draw circle */ }}

public class Rectangle implements Shape { @Override public void draw() { /* draw rectangle */ }}

public class ShapeDrawer { public void draw(Shape shape) { shape.draw(); }}

Now to add a new shape (e.g., Triangle), you don't need to modify existing code — simply create a new class implementing the Shape interface.



3. Liskov Substitution Principle (LSP)

Theory: Objects in a program should be replaceable with instances of their subtypes without altering the correctness of the program. If S is a subtype of T, then objects of type T may be replaced with objects of type S without breaking functionality.

Classic LSP Violation:

public class Rectangle {    protected int width, height;    public void setWidth(int w) { width = w; }    public void setHeight(int h) { height = h; }    public int getArea() { return width * height; }}

public class Square extends Rectangle { @Override public void setWidth(int w) { super.setWidth(w); super.setHeight(w); } @Override public void setHeight(int h) { super.setWidth(h); super.setHeight(h); }}

A client working with Rectangle expects that changing the width does not affect the height. The Square violates this expectation. Solution: Use a common interface or separate classes without inheritance.



4. Interface Segregation Principle (ISP)

Theory: Clients should not depend on methods they do not use. It is better to have several specialized interfaces than one "fat" interface.

ISP Violation:

                

Blogs

Book Recommendations