One reason to change
A change in payment policy should not force a shipment-specific class to change with it.
SRP is often summarized as “a class should have one reason to change.” That wording becomes useful when you interpret it through business responsibility instead of line count. A class can be small and still contain mixed concerns. It can be fairly large and still be cohesive if all of its behavior serves one clear purpose.
In order-management systems, one workflow often drifts into pricing, payment, shipment, fraud, notification, persistence, and metrics. SRP helps you stop asking “is this class too long?” and start asking “does this behavior belong to the same responsibility boundary?”
SRP is a cohesion principle. It asks whether a unit of code answers one main design question. In OOAD work, that usually means one business concern, one coordination concern, or one focused policy concern.
In Java, SRP often leads to cleaner use-case classes, focused domain policies, and better package boundaries. It does not mean every helper becomes a new class. Splitting behavior only helps if the new boundaries make the model easier to explain and evolve.
The best SRP test is often conversational: can you explain why this class exists in one sentence without using “and” three times?
A change in payment policy should not force a shipment-specific class to change with it.
The question is whether the behavior belongs together, not whether the class fits an arbitrary length rule.
SRP creates seams for testing, extension, and ownership when responsibilities are truly distinct.
package org.javaomnibus.ecommerce.principles;
public final class CheckoutCoordinator {
private final OrderPricingService orderPricingService;
private final PaymentAuthorizer paymentAuthorizer;
private final ShipmentStarter shipmentStarter;
public CheckoutCoordinator(
OrderPricingService orderPricingService,
PaymentAuthorizer paymentAuthorizer,
ShipmentStarter shipmentStarter
) {
this.orderPricingService = orderPricingService;
this.paymentAuthorizer = paymentAuthorizer;
this.shipmentStarter = shipmentStarter;
}
public void checkout(Order order) {
orderPricingService.calculate(order);
paymentAuthorizer.authorize(order);
shipmentStarter.start(order);
}
}
interface OrderPricingService { void calculate(Order order); }
interface PaymentAuthorizer { void authorize(Order order); }
interface ShipmentStarter { void start(Order order); }
record Order(String orderId) {}
Taken too literally, SRP can fragment a design into dozens of tiny types with weak conceptual separation. The principle works best when you use it to improve cohesion, not to maximize the number of classes.