DEV Community

Jay D
Jay D

Posted on

Single Responsibility Principle in Java

Hey there! When you're just starting out with coding, it's easy to write code that works but is hard to read or change later. that's where SOLID Principles come in ! They are like rules to help you write clean and maintainable code.
Imagine we're building a ride sharing app(like Uber)

What Does SOLID Stand for?
S: Single Responsibility Principal
O: Open/Closed Principle
L: Liskov Substitution Principle
I: Interface Segregation Principle
D: Dependency Inversion Principle

1. Single Responsibility Principal (SRP)

Rule: One Class Should do only one job
Problem: The Overloaded Class
Imagine a class in your ride-sharing app that handles booking rides, payments and rating drivers.

class RideService {
       void bookRide(){/* Logic for booking ride */}
       void processPayment(){/* Logic for process payment */}
       void rateDriver(){/* Logic for reviews */}
}

Enter fullscreen mode Exit fullscreen mode

If your payment system changes, you risk breaking the ride-booking code.
That's bad!

Fix: Divide the work:

class RideBooking {
   void bookRide(){/* Only handles booking ride */}
}

class PaymentService{
   void processPayment(){/* Only handles payment process */}
}

class DriverRating{
  void rateDriver(){/* Only handle reviews */}
}
Enter fullscreen mode Exit fullscreen mode

Now, If something goes wrong with payments, it won't mess up ride booking.
Everyone sticks to their job!

2. Open/Closed Principle
Rule: Classes should be open for extension but closed for modification
Problem: Your payment system initially supports only Credit card payments. Adding support for PayPal requires modifying existing code, which could introduce bugs.

class PaymentProcesser{
      double processPayments(String type){
          if(type.equals("CrediCard")){
             return 90.0;          
         }else if (type.equals("PayPal")){
             return 90.0;
        }
       return 0.0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Fix: Use an interface to make the system extensible without modifying existing code.

interface PaymenTMetod{
   double processPayment();
}

class CrediCardPayment implements PaymentMethod{
     public double processPayment() { return 90.0; }
}

class PayPalPayment implements PaymentMethod{
     public double processPayment() { return 95.0; }
}

class PaymentProcessor{
     double process(PaymentMethod payment){
        return payment.processPayment();
     }
}
Enter fullscreen mode Exit fullscreen mode

3. Liskov Substitution Principle
Rule: If something works with a parent class,
it should also work with its child classes
Problem: Unpredictable Vehicles
Your app allows people to book cars, bikes, or trucks.
But one day, someone tries to book a truck for a ride,
and the app crashes.
Why? Because trucks aren’t supposed to take passengers.

class Vehicle {
    void startRide() { System.out.println("Ride started"); }
}

class Truck extends Vehicle {
    @Override
    void startRide() {
        throw new UnsupportedOperationException("Trucks can't be booked");
    }
}
Enter fullscreen mode Exit fullscreen mode

When you use Truck like any other vehicle, it doesn’t work as expected.

Fix:

Let’s ensure that every type of Vehicle behaves as it should.

abstract class Vehicle {
    abstract void startRide();
}

class Car extends Vehicle {
    void startRide() { System.out.println("Car ride started"); }
}

class Bike extends Vehicle {
    void startRide() { System.out.println("Bike ride started"); }
}
Enter fullscreen mode Exit fullscreen mode

Now, only vehicles that can take rides are part of the system. Everyone plays fair!

4. Interface Segregation Principle
Rule: A class should not be forced to implement methods that doesnt use.
Problem: Too Many Responsibilities
Drivers in your app can accept rides and see earnings,
while admins can manage drivers.
If you give both the same interface, it looks like this:

interface AppUser {
    void acceptRide();
    void viewEarnings();
    void manageDrivers();
}
Enter fullscreen mode Exit fullscreen mode

Drivers don’t manage drivers,
so they leave that method empty or throw an error. That’s awkward!

Fix: Keep Interfaces Small

interface RideHandler {
    void acceptRide();
}

interface EarningsViewer {
    void viewEarnings();
}

interface DriverManager {
    void manageDrivers();
}

class Driver implements RideHandler, EarningsViewer {
    public void acceptRide() { System.out.println("Ride accepted"); }
    public void viewEarnings() { System.out.println("Earnings displayed"); }
}

class Admin implements DriverManager {
    public void manageDrivers() { System.out.println("Managing drivers"); }
}
Enter fullscreen mode Exit fullscreen mode

Now, drivers only do what they’re supposed to, and admins handle their tasks separately. Everyone’s happy!

5. Dependency Inversion Principle
Rule: High-level module should not depend on low level modules. Both should depend on abstraction.
Problem: Hard-Coded Notifications
Your app sends SMS notifications, but now you want to add email and push notifications. Your code is tightly tied to SMS:

class SMSNotification {
    void send(String message) { System.out.println("SMS: " + message); }
}

class NotificationManager {
    private SMSNotification smsNotification;

    public NotificationManager() {
        this.smsNotification = new SMSNotification();
    }

    void notifyUser(String message) {
        smsNotification.send(message);
    }
}

Enter fullscreen mode Exit fullscreen mode

To add email, you’d have to rewrite everything. What a pain!

Fix: Use an abstraction to decouple the notification manager from specific notification methods.

interface NotificationService {
    void send(String message);
}

class SMSNotification implements NotificationService {
    public void send(String message) { System.out.println("SMS: " + message); }
}

class EmailNotification implements NotificationService {
    public void send(String message) { System.out.println("Email: " + message); }
}

class NotificationManager {
    private NotificationService notificationService;

    public NotificationManager(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    void notifyUser(String message) {
        notificationService.send(message);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, you can easily switch between SMS, email, or any other notification method.

Why You Should Care

Following SOLID principles makes your code:

Easy to understand (no messy “spaghetti” code).

Easy to update (add features without fear).

Fun to work with (seriously, your future self will thank you).

So next time you build an app or write code, remember these five principles. They’ll make you the smartest coder in the room—and who doesn’t want that?

What principle do you think is the coolest? Let me know!

Top comments (0)