Single Responsibility Principle Explained
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
The Single Responsibility Principle says a class or module should have one primary reason to change. In practical terms, its methods should serve one cohesive concern or actor. It may have several methods; they belong together when the same policy change affects them.
For example, a controller can translate an HTTP request, an order service can apply the order use case, a repository can persist orders, and a notification adapter can call an email provider. If endpoint formatting, tax rules, SQL mapping, and email templates all live in one class, independent changes collide and tests need unrelated setup.
SRP is not a command to create one class per method. The useful boundary follows change: keep code together when it evolves together, and separate concerns that have different rules, owners, or external dependencies.
Single Responsibility Principle Explained
Direct Answer
The Single Responsibility Principle says a class or module should be responsible to one main actor or cohesive concern, giving it one primary reason to change. It can have many methods, but they should change together for the same purpose. Mixing HTTP handling, business rules, persistence, and email delivery usually creates several independent reasons to change.
Code Example
// VIOLATION: One class doing three things
class UserService {
public void registerUser(String email, String password) {
// Responsibility 1: Business logic
if (password.length() < 8) throw new IllegalArgumentException("Weak password");
var user = new User(email, hashPassword(password));
// Responsibility 2: Database access
Connection conn = DriverManager.getConnection("jdbc:...");
PreparedStatement stmt = conn.prepareStatement("INSERT INTO users...");
stmt.executeUpdate();
// Responsibility 3: Email notification
Session session = Session.getInstance(props);
MimeMessage message = new MimeMessage(session);
Transport.send(message); // Three reasons to change this class!
}
}
// COMPLIANT: Each class has one responsibility
record User(String email, String hashedPassword) {}
@Service
class UserRegistrationService {
private final UserRepository repository;
private final EmailService emailService;
public void registerUser(String email, String password) {
validatePassword(password); // Single responsibility: orchestration
var user = new User(email, hashPassword(password));
repository.save(user);
emailService.sendWelcomeEmail(email);
}
}
@Repository
class UserRepository {
public void save(User user) { /* DB logic only */ }
}
@Service
class EmailService {
public void sendWelcomeEmail(String email) { /* Email logic only */ }
}
Common Mistakes & Pitfalls
- Confusing SRP with 'one method per class': Freshers often think SRP means tiny classes with single methods, leading to absurd over-granularity. A UserRepository with save, findById, delete is perfectly SRP-compliant because all methods serve one responsibility: User data access.
- Naming classes with vague suffixes: Using names like UserManager, DataHandler, or ServiceHelper hides the real responsibility. These become dumping grounds for unrelated methods.
- Mixing layers in one class: Putting @RestController, business logic, and @Repository annotations on the same class violates SRP by mixing presentation, domain, and persistence concerns. Each layer is a separate reason to change.
Check your understanding
Answer these without looking back at the article.
- How does SRP relate to cohesion?
- Can a class have multiple methods and still follow SRP?
- How do you identify an SRP violation in a code review?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.