DEV Community

Cover image for 5 Common Code Smells in Java and How to Fix Them
sachinkg12
sachinkg12

Posted on

5 Common Code Smells in Java and How to Fix Them

Code smells are indicators of potential problems in your code that could lead to issues with maintainability, readability, or performance. While they don't necessarily cause bugs, addressing code smells ensures your codebase remains clean and efficient.

We will look into 5 common code smells in Java, offering examples, in-depth explanations, and better approaches to handle them effectively.


1. Long Method

Code Smell: A method that is excessively long can make the code harder to read, test, and maintain. Even if the method appears modularized with helper methods, it might still combine multiple levels of abstraction, violating the Single Responsibility Principle (SRP).

Example:

public void processOrder(Order order) {
    validateOrder(order);
    calculateDiscount(order);
    updateInventory(order);
    generateInvoice(order);
    sendNotification(order);
}
Enter fullscreen mode Exit fullscreen mode

Here, processOrder orchestrates multiple unrelated tasks—validation, discount calculation, inventory update, invoice generation, and notification—making it difficult to extend or modify without risking unintended consequences.

Better Approach:

Refactor the orchestration logic to separate these responsibilities more clearly. Using design patterns such as the Command Pattern or Pipeline Pattern can help ensure modularity.

Refactored Code with Command Pattern:

public interface OrderCommand {
    void execute(Order order);
}

public class ValidateOrderCommand implements OrderCommand {
    public void execute(Order order) {
        // Validation logic
    }
}

public class ApplyDiscountCommand implements OrderCommand {
    public void execute(Order order) {
        // Discount application logic
    }
}

public class OrderProcessor {
    private List<OrderCommand> commands;

    public OrderProcessor(List<OrderCommand> commands) {
        this.commands = commands;
    }

    public void processOrder(Order order) {
        for (OrderCommand command : commands) {
            command.execute(order);
        }
    }
}

// Usage
List<OrderCommand> commands = List.of(
    new ValidateOrderCommand(),
    new ApplyDiscountCommand(),
    new UpdateInventoryCommand(),
    new GenerateInvoiceCommand(),
    new NotifyCustomerCommand()
);

OrderProcessor processor = new OrderProcessor(commands);
processor.processOrder(new Order());
Enter fullscreen mode Exit fullscreen mode

Benefit:

  • Improves modularity.
  • Each command handles a single responsibility and can be tested or reused independently.
  • Adding new steps (e.g., fraud detection) is as simple as adding a new OrderCommand.

2. God Class

Code Smell: A "God Class" tries to handle too many responsibilities, leading to high coupling and poor maintainability.

Example:

public class OrderManager {
    public void createOrder() { /* Implementation */ }
    public void updateOrder() { /* Implementation */ }
    public void deleteOrder() { /* Implementation */ }
    public void validatePayment() { /* Implementation */ }
    public void sendInvoice() { /* Implementation */ }
}
Enter fullscreen mode Exit fullscreen mode

Better Approach:

Divide the responsibilities into smaller, focused classes, each aligned with a specific domain task.

Refactored Code:

public class OrderService {
    public void createOrder() { /* Implementation */ }
    public void updateOrder() { /* Implementation */ }
    public void deleteOrder() { /* Implementation */ }
}

public class PaymentService {
    public void validatePayment() { /* Implementation */ }
}

public class NotificationService {
    public void sendInvoice() { /* Implementation */ }
}
Enter fullscreen mode Exit fullscreen mode

Benefit:

  • Reduces coupling and improves modularity.
  • Each service class becomes easier to maintain, test, and extend independently.

3. Magic Numbers

Code Smell: Using literal numbers (or "magic numbers") directly in your code makes it hard to understand and modify.

Example:

public double calculateDiscount(double totalAmount) {
    return totalAmount > 1000 ? totalAmount * 0.1 : totalAmount;
}
Enter fullscreen mode Exit fullscreen mode

Better Approach:

Replace magic numbers with constants that have meaningful names.

Refactored Code:

private static final double DISCOUNT_THRESHOLD = 1000;
private static final double DISCOUNT_RATE = 0.1;

public double calculateDiscount(double totalAmount) {
    return totalAmount > DISCOUNT_THRESHOLD ? totalAmount * DISCOUNT_RATE : totalAmount;
}
Enter fullscreen mode Exit fullscreen mode

Benefit:

  • Improves readability and reduces the risk of errors during updates.
  • Constants provide clarity about the business logic.

4. Duplicated Code

Code Smell: Repetitive code across methods or classes leads to maintenance challenges and inconsistency.

Example:

public double calculateTax(double amount) {
    return amount * 0.18;
}

public double calculateDiscount(double amount) {
    return amount * 0.1;
}
Enter fullscreen mode Exit fullscreen mode

Better Approach:

Abstract the common logic into a reusable method.

Refactored Code:

private double applyRate(double amount, double rate) {
    return amount * rate;
}

public double calculateTax(double amount) {
    return applyRate(amount, 0.18);
}

public double calculateDiscount(double amount) {
    return applyRate(amount, 0.1);
}
Enter fullscreen mode Exit fullscreen mode

Benefit:

  • Avoids redundancy.
  • Ensures consistency across methods.
  • Makes the code easier to modify and extend.

5. Excessive Parameter List

Code Smell: Methods with too many parameters are difficult to read, understand, and prone to errors during calls.

Example:

public void createUser(String firstName, String lastName, String email, String phoneNumber, String address) {
    // Implementation
}
Enter fullscreen mode Exit fullscreen mode

Better Approach:

Encapsulate parameters in an object or use a builder pattern.

Refactored Code:

public class User {
    private String firstName;
    private String lastName;
    private String email;
    private String phoneNumber;
    private String address; // Getters, setters, and constructor
}

public void createUser(User user) {
    // Implementation
}
Enter fullscreen mode Exit fullscreen mode

Benefit:

  • Improves readability and extensibility.
  • Additional parameters can be added without changing the method signature.

Code smells are early indicators of deeper design issues that can impact maintainability and scalability. By identifying and addressing these smells, you improve code quality and reduce technical debt.

Using principles like DRY (Don't Repeat Yourself), SRP (Single Responsibility Principle), and modular design, you can build robust, clean, and efficient Java applications.

Have you encountered these code smells in your projects? Share your experiences and solutions in the comments below!

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up