Building Better Code: SOLID Principles in Java

Arpit Bhatt
4 min readJan 27, 2025

--

As a Java developer, creating maintainable, scalable, and robust applications is paramount. Adopting SOLID principles — a set of five design guidelines introduced by Robert C. Martin (Uncle Bob) — can significantly improve your object-oriented programming skills. These principles help reduce code smells, enhance testability, and promote better software design. Let’s explore each principle with Java-focused examples.

1. Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change. In other words, a class should only have one responsibility.

Example:

// Violation of SRP
class Employee {
private String name;
private String position;
public void saveToDatabase() {
// Code to save employee details to the database
}
public void generateReport() {
// Code to generate an employee report
}
}
// Refactored to follow SRP
class Employee {
private String name;
private String position;
// Employee-specific attributes and behaviors
}
class EmployeeRepository {
public void saveToDatabase(Employee employee) {
// Code to save employee details to the database
}
}
class EmployeeReport {
public void generateReport(Employee employee) {
// Code to generate an employee report
}
}

By separating responsibilities, we make the code easier to manage and test.

2. Open/Closed Principle (OCP)

Definition: Software entities should be open for extension but closed for modification.

Example:

// Violation of OCP
class Calculator {
public double calculate(String operation, double a, double b) {
if (operation.equals("add")) {
return a + b;
} else if (operation.equals("subtract")) {
return a - b;
}
throw new UnsupportedOperationException("Operation not supported");
}
}

// Refactored to follow OCP
interface Operation {
double execute(double a, double b);
}
class Addition implements Operation {
public double execute(double a, double b) {
return a + b;
}
}
class Subtraction implements Operation {
public double execute(double a, double b) {
return a - b;
}
}
class Calculator {
public double calculate(Operation operation, double a, double b) {
return operation.execute(a, b);
}
}

By using polymorphism, we can add new operations without modifying existing code.

3. Liskov Substitution Principle (LSP)

Definition: Subtypes must be substitutable for their base types without altering the correctness of the program.

Example:

// Violation of LSP
class Rectangle {
private int width;
private int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
class Square extends Rectangle {
@Override
public void setWidth(int width) {
super.setWidth(width);
super.setHeight(width);
}
@Override
public void setHeight(int height) {
super.setWidth(height);
super.setHeight(height);
}
}

The above example violates LSP because substituting Square for Rectangle may produce unexpected results. A better approach is to design a hierarchy that respects the principle:

interface Shape {
int getArea();
}

class Rectangle implements Shape {
private int width;
private int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
class Square implements Shape {
private int side;
public void setSide(int side) {
this.side = side;
}
public int getArea() {
return side * side;
}
}

4. Interface Segregation Principle (ISP)

Definition: A class should not be forced to implement interfaces it does not use.

Example:

// Violation of ISP
interface Worker {
void work();
void eat();
}

class Robot implements Worker {
public void work() {
// Robot working
}
public void eat() {
// Robots don't eat, but must implement this method
}
}
// Refactored to follow ISP
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Human implements Workable, Eatable {
public void work() {
// Human working
}
public void eat() {
// Human eating
}
}
class Robot implements Workable {
public void work() {
// Robot working
}
}

By creating smaller, specific interfaces, we avoid unnecessary method implementations.

5. Dependency Inversion Principle (DIP)

Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.

Example:

// Violation of DIP
class Keyboard {
public String getInput() {
return "Input from keyboard";
}
}

class Monitor {
public void display(String content) {
System.out.println(content);
}
}
class Computer {
private Keyboard keyboard;
private Monitor monitor;
public Computer() {
this.keyboard = new Keyboard();
this.monitor = new Monitor();
}
public void start() {
String input = keyboard.getInput();
monitor.display(input);
}
}
// Refactored to follow DIP
interface InputDevice {
String getInput();
}
interface OutputDevice {
void display(String content);
}
class Keyboard implements InputDevice {
public String getInput() {
return "Input from keyboard";
}
}
class Monitor implements OutputDevice {
public void display(String content) {
System.out.println(content);
}
}
class Computer {
private InputDevice inputDevice;
private OutputDevice outputDevice;
public Computer(InputDevice inputDevice, OutputDevice outputDevice) {
this.inputDevice = inputDevice;
this.outputDevice = outputDevice;
}
public void start() {
String input = inputDevice.getInput();
outputDevice.display(input);
}
}

Using abstractions makes the code more flexible and easier to extend or modify.

Conclusion

By adhering to the SOLID principles, you can design better Java applications that are easier to maintain, test, and extend. These principles encourage clean code practices and help in building a strong foundation for long-term software development projects. Start applying these principles in your codebase to experience their benefits firsthand!

Happy coding!

— — — — — — — —

Follow my Instagram page — Programming_Pulse for daily programming tips and insights!

https://www.instagram.com/programming_pulse/

--

--

Arpit Bhatt
Arpit Bhatt

No responses yet