| name | clean-code |
| description | SOLID principles, clean code practices, and refactoring patterns for Java applications |
Clean Code for Java
SOLID Principles
Single Responsibility Principle
Each class should have only one reason to change.
public class OrderService {
public Order createOrder(CreateOrderRequest request) { }
public void sendOrderConfirmationEmail(Order order) { }
}
public class OrderService {
private final OrderNotificationService notificationService;
public Order createOrder(CreateOrderRequest request) {
var order = processOrder(request);
notificationService.notifyOrderCreated(order);
return order;
}
}
public class OrderNotificationService {
private final EmailService emailService;
public void notifyOrderCreated(Order order) {
emailService.send(buildConfirmationEmail(order));
}
}
Open/Closed Principle
Open for extension, closed for modification.
public interface DiscountStrategy {
BigDecimal calculateDiscount(Order order);
}
@Component
public class VolumeDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculateDiscount(Order order) {
if (order.getItemCount() > 10) {
return order.getTotal().multiply(new BigDecimal("0.10"));
}
return BigDecimal.ZERO;
}
}
@Component
public class LoyaltyDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculateDiscount(Order order) {
if (order.getCustomer().isLoyal()) {
return order.getTotal().multiply(new BigDecimal("0.05"));
}
return BigDecimal.ZERO;
}
}
@Service
public class DiscountService {
private final List<DiscountStrategy> strategies;
public BigDecimal calculateTotalDiscount(Order order) {
return strategies.stream()
.map(s -> s.calculateDiscount(order))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
Liskov Substitution Principle
Subtypes must be substitutable for their base types.
public class Rectangle {
public void setWidth(int w) { this.width = w; }
public void setHeight(int h) { this.height = h; }
}
public sealed interface Shape permits Rectangle, Square, Circle {
double area();
}
public record Rectangle(double width, double height) implements Shape {
public double area() { return width * height; }
}
public record Square(double side) implements Shape {
public double area() { return side * side; }
}
Interface Segregation Principle
Prefer small, focused interfaces.
public interface UserService {
User findById(String id);
List<User> findAll();
void createUser(User user);
void deleteUser(String id);
void sendWelcomeEmail(User user);
void generateReport(User user);
}
public interface UserReadService {
User findById(String id);
List<User> findAll();
}
public interface UserWriteService {
void createUser(User user);
void deleteUser(String id);
}
Dependency Inversion Principle
Depend on abstractions, not concretions.
@Service
public class PaymentService {
private final PaymentGateway paymentGateway;
public PaymentResult processPayment(PaymentRequest request) {
return paymentGateway.charge(request);
}
}
@Component
@Profile("production")
public class StripePaymentGateway implements PaymentGateway {
@Override
public PaymentResult charge(PaymentRequest request) { }
}
Clean Code Practices
Meaningful Names
int d;
List<int[]> list1;
public void process(Map<String, Object> m) {}
int elapsedDays;
List<Cell> flaggedCells;
public void processPayment(Map<String, PaymentDetail> paymentsByOrderId) {}
Small, Focused Methods
public void processOrder(Order order) {
}
public void processOrder(Order order) {
validateOrder(order);
var totals = calculateTotals(order);
var finalAmount = applyDiscounts(order, totals);
persistOrder(order, finalAmount);
notifyOrderCreated(order);
}
Avoid Null
public User findUser(String id) {
return userMap.get(id);
}
public Optional<User> findUser(String id) {
return Optional.ofNullable(userMap.get(id));
}
public List<Order> findOrders(String customerId) {
var orders = orderRepository.findByCustomerId(customerId);
return orders != null ? orders : List.of();
}
Refactoring Patterns
- Extract Method -- Break long methods into smaller, named steps
- Replace Conditional with Polymorphism -- Use strategy or visitor pattern
- Introduce Parameter Object -- Group related parameters into a record
- Replace Magic Numbers -- Use named constants or enums
- Guard Clauses -- Return early to reduce nesting