System Design

SOLID is a set of five principles for designing software that is easy to understand, maintain, and extend. These principles are often used in object-oriented design and serve as guidelines for creating flexible and robust systems.

SOLID Principles

  1. Single Responsibility Principle (SRP)

  2. Open/Closed Principle (OCP)

  3. Liskov Substitution Principle (LSP)

  4. Interface Segregation Principle (ISP)

  5. Dependency Inversion Principle (DIP)

These principles make systems more modular, maintainable, and scalable, reducing the chances of bugs and simplifying future development.

1. Single Responsibility Principle (SRP)

Principle: A class should have only one reason to change, i.e., it should have only one responsibility.

In a car rental service, we could have a CarService that manages car-related logic (like availability) and a separate RentalService that handles rental-related logic.

Code Example:

// CarService.java
@Service
public class CarService {
    public boolean isCarAvailable(String carId) {
        // Check if the car is available for rent
        return true; // Assume car is available
    }
}

// RentalService.java
@Service
public class RentalService {
    private final CarService carService;

    public RentalService(CarService carService) {
        this.carService = carService;
    }

    public void rentCar(String carId, String userId) {
        if (carService.isCarAvailable(carId)) {
            // Logic to rent the car
            System.out.println("Car rented successfully.");
        } else {
            System.out.println("Car not available.");
        }
    }
}

Explanation: CarService only checks the car's availability, while RentalService handles the rental logic. Each service has a single responsibility.


2. Open/Closed Principle (OCP)

Principle: Classes should be open for extension but closed for modification.

In a car rental service, suppose we want to calculate different rental prices based on car types. Instead of modifying a single pricing class each time we add a new car type, we can extend it.

Code Example:

Explanation: If a new car type is added, like SportsCarPriceCalculator, we create a new class implementing RentalPriceCalculator without modifying the existing ones.


3. Liskov Substitution Principle (LSP)

Principle: Objects of a superclass should be replaceable with objects of its subclasses without affecting the system.

In our car rental system, if ElectricCar is a subtype of Car, it should work in the same way as a Car when rented, without breaking any functionality.

Code Example:

Explanation: Both ElectricCar and DieselCar can be used wherever a Car type is expected without breaking the code, thus adhering to Liskov Substitution.


4. Interface Segregation Principle (ISP)

Principle: Clients should not be forced to depend on methods they do not use.

In our car rental example, a Car interface could have separate interfaces like FuelCar and ElectricCar. This way, electric cars don’t need to implement fuel-related methods.

Code Example:

Explanation: FuelCar has a refuel() method, while ElectricCar has a recharge() method. Tesla, being an electric car, does not need to implement refuel().


5. Dependency Inversion Principle (DIP)

Principle: High-level modules should not depend on low-level modules but rather on abstractions.

In our car rental system, the RentalService should depend on an NotificationService interface rather than a specific implementation like EmailNotificationService.

Code Example:

Explanation: RentalService depends on the NotificationService abstraction, so we can switch to any other notification implementation, like SMSNotificationService, without changing RentalService.


Summary Table

SOLID Principle
Car Rental Service Example

Single Responsibility

CarService checks availability, RentalService handles rentals.

Open/Closed

RentalPriceCalculator extended by StandardCarPriceCalculator, etc.

Liskov Substitution

ElectricCar and DieselCar are interchangeable as Car types.

Interface Segregation

FuelCar and ElectricCar interfaces avoid unused methods.

Dependency Inversion

RentalService depends on NotificationService abstraction.

This unified car rental example demonstrates each SOLID principle in Spring Boot for a maintainable, scalable system.

Last updated