Identifying SRP Violations in Java Code
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
I identify an SRP violation by asking which independent reasons can change the class. If endpoint representation, pricing policy, SQL mapping, and email templates all require edits to one class, it is serving several concerns.
The code often confirms that result: one group of methods uses a repository, another uses a mail client, and another only formats HTTP responses. Tests for pricing may need unrelated mail or database setup. Those clusters suggest boundaries such as a pricing policy, repository adapter, and notification service.
Size and counts are not rules. A large parser can be cohesive, and a small utility can mix unrelated behavior. I use change history, ownership, dependencies, and method-field relationships as evidence, then extract only when the new class has a clear responsibility and contract.
Identifying SRP Violations in Java Code
Direct Answer
Identify an SRP violation by listing the independent reasons, actors, or policies that can change a class. Mixed responsibilities often show up as unrelated dependency groups, methods that use disjoint fields, or tests that require unrelated setup. File length, import count, field count, and names containing 'and' are clues only—not numeric rules or proof.
Code Example
// VIOLATION SIGNALS: Multiple unrelated imports
import javax.mail.*;
import java.sql.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.*;
@RestController // Smell: Multiple stereotypes in one class
class OrderProcessor {
private DataSource dataSource; // Database concern
private JavaMailSender mailSender; // Email concern
private ObjectMapper jsonMapper; // Serialization concern
private PaymentGateway paymentGateway; // External API concern
@PostMapping("/orders") // Responsibility 1: HTTP handling
public String createOrder(@RequestBody String json) throws Exception {
Order order = jsonMapper.readValue(json, Order.class); // Responsibility 2: Parsing
if (order.total() < 0) return "Invalid"; // Responsibility 3: Validation
Connection conn = dataSource.getConnection(); // Responsibility 4: DB access
conn.prepareStatement("INSERT INTO orders...").executeUpdate();
paymentGateway.charge(order.total()); // Responsibility 5: External API
MimeMessage msg = mailSender.createMimeMessage(); // Responsibility 6: Email
msg.setSubject("Order confirmed");
mailSender.send(msg);
return "Success"; // Six reasons to change!
}
}
// Change test: This class changes when:
// 1. REST API contract changes
// 2. JSON format changes
// 3. Validation rules change
// 4. Database schema changes
// 5. Payment provider changes
// 6. Email template changes
Common Mistakes & Pitfalls
- Using file size as proof: a long cohesive parser may be fine, while a short class can mix policies.
- Treating imports or field count as numeric rules: look for independent dependency clusters and change reasons.
- Splitting on vague names alone: confirm the boundary with usage and change history.
- Producing smaller classes with no clear responsibility: extraction should make ownership and contracts clearer.
Check your understanding
Answer these without looking back at the article.
- What tools can help detect SRP violations automatically?
- Is it always bad to have a large class?
- How does SRP violation affect unit testing?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.